题目描述

给定 n 和 n 个信息,每个信息包含一个词性 a (只有三种:名,动,辅)和对应的词 mot ,形为“ \(a.mot\) ”。(一次可能多词性)

最后给一个长度不大于 \(5KB\) 的冰峰文文章,将这篇冰峰文文章划分为最少的句子,在这个前提下,将文章划分为最少的单词时,求划分的句子数量和单词数量。

划分标准:



(别问我为什么盗图。。

\(1\leq n\leq 10^3\ \ \ \ mot.len\leq 20\)

\(solution\)

首先要搞懂题目中的图是什么玩意(我真的看了好久都没看懂。。)

所有语法简述下来就是:


1.名词短语是许多个辅词加一个名词组成的。

2.动词短语是许多个辅词加一个动词组成的。

3.一个句子以名词短语开头,名词短语和动词短语交替出现而组成的。

4.文章为多句话组成。


所以对于任意词,有四种类型:


1.名词。

2.动词。

3.辅名词的辅词。

4.辅动词的辅词。


状态应该很自然了。。(要什么设什么呗)

\(f[j][i][0]\) 指前 \(i\) 个字母,最后一个单词是名词,构成了 \(j\) 个句子的最小单词数。

\(f[j][i][1]\) 指前 \(i\) 个字母,最后一个单词是动词,构成了 \(j\) 个句子的最小单词数。

\(f[j][i][2]\) 指前 \(i\) 个字母,最后一个单词是辅词,后面要接动词,构成了 \(j\) 个句子的最小单词数。

\(f[j][i][3]\) 指前 \(i\) 个字母,最后一个单词是辅词,后面要接名词,构成了 \(j\) 个句子的最小单词数。

状态转移方程就按照语法看能否转移就行

\(f[j][i][0] \Longrightarrow \min{(f[j][k][1/3],f[j-1][k][0/2])}\)

\(f[j][i][1] \Longrightarrow \min{(f[j][k][0/2])}\)

\(f[j][i][2] \Longrightarrow \min{(f[j][k][0/2])}\)

\(f[j][i][3] \Longrightarrow \min{(f[j][k][1/3],f[j-1][k][0/1])}\)

实际上看式子的话, \(j\) 那一维可以滚动起来。(虽然不滚掉好像问题不大,但省空间多好。。)

最后答案就是按题目来,求一个最小的 \(ans\) ,存在 \(f[ans][len][0/1]\) ,如果都存在,取较小值。

\(DP\) 这里就结束了,考虑如何实现。

明显 \(DP\) 的复杂度不允许我们每次枚举所有单词再去比较。

所以想到了用一个比较实用的东西 \(trie\) 可以把速度拉起来。

基本上这题就搞定了,就是注意一定把数组开稍微大点(我因为忽略数组大小而傻乎乎地去调了半个小时程序了)

\(code\)

#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int N=1e3+10,M=6e3+10,K=3e4+10;
const int INF=0x3f3f3f3f;
int n,m,mlth,f[2][M][4],tri[M][24],tot,op,ans1,ans2;
char sw[M],sd[M];
struct trie{
int tr[26],opt,it;
inline void clear(){
memset(tr,0,sizeof(tr));
it=-1;opt=0;
}
}trie[K];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void insert(char s[],int lth,int opt){
int id=0,val;
for(int i=2;i<lth;++i){
val=s[i]-'a';
if(!trie[id].tr[val]){
trie[++tot].clear();
trie[tot].it=val;
trie[id].tr[val]=tot;
}
id=trie[id].tr[val];
}
trie[id].opt|=opt;
}
inline int find(int lt,int rt){
int id=0,val;
for(int i=lt;i<=rt;++i){
val=sd[i]-'a';
if(!trie[id].tr[val])return 0;
id=trie[id].tr[val];
}
return trie[id].opt;
}
inline void mian(){
ans1=0;ans2=INF;
trie[0].clear();
memset(tri,-1,sizeof(tri));
memset(f,INF,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%s",sw);m=strlen(sw);
mlth=max(m,mlth);
if(sw[0]=='n')insert(sw,m,1);
else if(sw[0]=='v')insert(sw,m,2);
else if(sw[0]=='a')insert(sw,m,4);
}
scanf("%s",sd+1);m=strlen(sd+1)-1;
f[0][0][0]=0;
for(int lin=1;lin<=m;++lin){
int now=op^1,pre=op;
for(int i=1;i<=m;++i){
memset(f[now][i],INF,sizeof(f[now][i]));
int lim=max(i-mlth,0);
for(int j=i-1;j>=lim;--j){
if(tri[j+1][i-j]==-1)
tri[j+1][i-j]=find(j+1,i);
int opti=tri[j+1][i-j],nowi,prei;
if(opti&1){
nowi=min(f[now][j][1],f[now][j][3]);
prei=min(f[pre][j][0],f[pre][j][2]);
f[now][i][0]=min(f[now][i][0],nowi+1);
f[now][i][0]=min(f[now][i][0],prei+1);
}//不能有else
if(opti&2){
nowi=min(f[now][j][0],f[now][j][2]);
f[now][i][1]=min(f[now][i][1],nowi+1);
}//不能有else
if(opti&4){
nowi=min(f[now][j][0],f[now][j][2]);
f[now][i][2]=min(f[now][i][2],nowi+1); nowi=min(f[now][j][1],f[now][j][3]);
prei=min(f[pre][j][0],f[pre][j][1]);
f[now][i][3]=min(f[now][i][3],nowi+1);
f[now][i][3]=min(f[now][i][3],prei+1);
}//不能有else
}
}
ans2=min(f[now][m][0],f[now][m][1]);
if(ans2!=INF){ans1=lin;break;}
op^=1;
}
printf("%d\n%d\n",ans1,ans2);
}
int main(){
n=read();
mian();
return 0;
}

[NOI2000] 古城之谜的更多相关文章

  1. hdu4843(NOI2000) 古城之谜 (trie树+DP)

    Description 著名的考古学家石教授在云梦高原上发现了一处古代城市遗址.让教授欣喜的是在这个他称为冰峰城(Ice-Peak City)的城市中有12块巨大石碑,上面刻着用某种文字书写的资料,他 ...

  2. dp式子100个……

    1.        资源问题1-----机器分配问题F[I,j]:=max(f[i-1,k]+w[i,j-k]) 2.        资源问题2------01背包问题F[I,j]:=max(f[i- ...

  3. dp方程

    1.        资源问题1 -----机器分配问题 F[I,j]:=max(f[i-1,k]+w[i,j-k]) 2.        资源问题2 ------01背包问题   F[I,j]:=ma ...

  4. pythonchallenge 解谜 Level 0

    解谜地址: http://www.pythonchallenge.com/pc/def/0.html 这题没什么难度,意思就是得到2的38次方的值,然后,替换 http://www.pythoncha ...

  5. pythonchallenge 解谜

    所有代码均使用python 3.5.1 版本 最近在学python,闲来无事觉得这个解谜还挺有意思. 解谜网址  http://www.pythonchallenge.com/ 接下来会写破解教程~

  6. 揭秘JavaScript中谜一样的this

      揭秘JavaScript中谜一样的this 在这篇文章里我想阐明JavaScript中的this,希望对你理解this的工作机制有一些帮助.作为JavaScript程序员学习this对于你的发展有 ...

  7. Activity的"singleTask"之谜

    官方文档称 以这种方式启动的Activity总是属于一个任务的根Activity.果真如此吗?本文将为你解开Activity的"singleTask"之谜. 任务(Task)是个什 ...

  8. Microsoft HoloLens 技术解谜(下)

    读者提问之“HoloLens 的深度传感器有没有可能是基于 TOF?” 先介绍下背景知识,市面上常见的有三种类型的深度传感器: 结构光,这个技术的代表产品是 Kinect 一代,它的传感器芯片用的是 ...

  9. 揭开Linux操作系统的Swap交换区之谜

    揭开Linux操作系统的Swap交换区之谜 Swap,即交换区,除了安装Linux的时候,有多少人关心过它呢?其实,Swap的调整对Linux服务器,特别是Web服务器的性能至关重要.通过调整Swap ...

随机推荐

  1. 论文阅读:MDNet: Learning Multi-Domain Convolutional Neural Networks for Visual Tracking

    前言 CVPR2016 来自Korea的POSTECH这个团队   大部分算法(例如HCF, DeepLMCF)只是用在大量数据上训练好的(pretrain)的一些网络如VGG作为特征提取器,这些做法 ...

  2. HTML5之WebSocket(转自知乎)

    在认识websocket之前,我们必须了解的是websocket有什么用? 他能解决我们遇到的什么问题? 如果没用,那么我们就么有使用它的必要的. websocket就是建立起全双工协议的,提高了效率 ...

  3. 狂神说redis笔记(四)

    十二.Redis主从复制 概念 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器.前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), ...

  4. 九、部署audit监控文件

    审计的目的是基于事先配置的规则生成日志,记录可能发生在系统上的事件(正常或非正常行为的事件),审计不会为系统提供额外的安全保护,但她会发现并记录违反安全策略的人及其对应的行为. 审计能够记录的日志内容 ...

  5. 一文彻底理解Apache Hudi的多版本清理服务

    Apache Hudi提供了MVCC并发模型,保证写入端和读取端之间快照级别隔离.在本篇博客中我们将介绍如何配置来管理多个文件版本,此外还将讨论用户可使用的清理机制,以了解如何维护所需数量的旧文件版本 ...

  6. 【NX二次开发】点到矢量或直线的垂点

    已知p1.p2.v1求p0 求解: 通过P1点和P2点,先求出v2: 使用 UF_VEC3_dot(),v1点乘v2得到P0与P2之间的距离: 使用UF_VEC3_unitize()将v1单位化: p ...

  7. UF_CLONE 克隆操作

    Open C UF_CLONE_add_assembly  添加装配到克隆操作UF_CLONE_add_part           添加部件到克隆操作UF_CLONE_apply_defaultsU ...

  8. redis学习第一天

    不同于其他的常用关系型数据库,redis是一个非常轻便,体积小,存放键值对的数据库,常用于构建高性能,可扩展的Web应用程序. 这是我第一次接触redis,之前没有使用过,只听说过.因为刚毕业,找工作 ...

  9. 开源版本的 uTools。可支持 uTools 所有插件生态

    话不多说,先放上截图和仓库地址: 代码仓库:github 故事背景 网络抓包 之前公司内部因为开发需要,需要和后端进行接口联调,测试环境的时候,经常会涉及到一些状态改变要看交互样式的问题.比如测试需要 ...

  10. 让Qt5默认支持C++17的设置方法

    单个项目的设置方法 打开项目的 pro 文件,将CONFIG += console c++11改为CONFIG += console c++17即可. 永久默认支持 c++17 的设置方法 找到 Qt ...