1.codevs1040 统计单词个数

1040 统计单词个数

2001年NOIP全国联赛提高组

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
题目描述 Description

给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。

输入描述 Input Description

第一行为一个正整数(0<n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。

输出描述 Output Description

每行一个整数,分别对应每组测试数据的相应结果。

 

样例输入 Sample Input

1
1 3
thisisabookyouareaoh
4
is
a
ok
sab

样例输出 Sample Output

7

数据范围及提示 Data Size & Hint

this/isabookyoua/reaoh

分类标签 Tags 点此展开

代码:
不知道为什么?把网站的测试数据下到自己电脑上以后,测试全部能通过,可是在线评测不是“运行错误”,就是“超时”。
/*基本思路:预处理一个 g[i][j]表示i--j这个区间内有多少个单词?我是用的
strstr, 函数完成的,寻找字串的位置、
怎么满足题目中要求的“当选用一个单词之后,其第一个字母不能再用,这里的不能再用指的是位置”?
我是设置了head这个标志位,既然这个首字母不能再用了,那么短的单词来充当这个位置,一定比长单词好,所以先把单词字典按照len排序,
DP方程: f[j][i]=max(f[j][i],f[t][i-1]+g[t+1][j]);
把前j个分为i份的最优值,是通过枚举把前t个分为i-1份,和剩下的部分分为1份来得到的
*/
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LEN 250
#define K 41
int n,p,k,s1;
struct ZD{
char s[LEN];
int len;
bool operator <(const ZD &q)
const{return len<q.len;}
};
ZD zd[];
int lenclac,f[LEN][K],g[LEN][LEN];
bool head[LEN];
char clac[LEN];
void input()
{
memset(head,,sizeof(head));
memset(clac,,sizeof(clac));
memset(zd,,sizeof(zd));
memset(g,,sizeof(g));
memset(f,,sizeof(f));
scanf("%d%d",&p,&k);
for(int i=;i<p;++i)
scanf("%s",clac+*i+);
lenclac=strlen(clac+);
scanf("%d",&s1);
for(int i=;i<=s1;++i)
{
scanf("%s",zd[i].s+);
zd[i].len=strlen(zd[i].s+);
}
clac[]='';
clac[lenclac+]='\0';
/*如果这里不给clac[0]赋值的话,那么下面的strcpy会出错*/
}
void chuli()
{
sort(zd+,zd+s1+);
for(int i=;i<=s1;++i)
{
char cpy[LEN];
strcpy(cpy,clac);
int p;
while()
{
p=strstr(cpy,zd[i].s+)-cpy;
if(p<) break;
if(head[p])
{
for(int j=;j<=p;++j)
cpy[j]='';
continue;}/*不加这个赋值是1,会陷入死循环*/
head[p]=true;
for(int j=;j<=p;++j)
cpy[j]='';
for(int j=p+zd[i].len-;j<=lenclac;++j)
{
g[p][j]++;
f[j][]++;
}
}
}
}
void DP()
{
for(int i=;i<=k;++i)
for(int j=i;j<=lenclac;++j)
{
for(int t=i-;t<=j-;++t)/*t这里必须循环到j-1而不是j,一开始就犯了这个错误,必须保证前j个分成i份才可以,循环到j,那可能是前j个分成了i-1份,*/
{
f[j][i]=max(f[j][i],f[t][i-]+g[t+][j]);
}
}
}
int main()
{ scanf("%d",&n);
while(n--)
{
input();
chuli();
DP();
printf("%d\n",f[lenclac][k]);
}
return ;
}
/*
把字符串ss[0..len-1]划分为k部分的最优值,需考虑
把前i个字符划分成j个部分的最优值
f(i,j) =Max{f(i-x,j-1)+后x个字符中的单词个数} (i>=j且x>=1,i-x>=j-1)
即1<=x<i-j
对于区间[ii..jj]中含有的单词个数,逐个统计以s[kk](ii<=kk<=jj)开头的单词即可,
*/
#include <stdio.h>
#define maxlen 210
#define maxk 41
#define maxs 10
int n,p,k,s,f[maxlen][maxk],len;
char ss[maxlen],tt[maxlen],w[maxs][maxlen];
void init(){
scanf("%d%d",&p,&k);
len=;
for(int i=;i<p;++i){
scanf("%s",tt);
for(int j=;tt[j];++j){
ss[len]=tt[j]; ++len;
}
}
ss[len]='\0';
scanf("%d",&s);
for(int i=;i<s;++i) scanf("%s",w[i]);
for(int i=;i<len;++i)
for(int j=;j<=k;++j) f[i][j]=;
}
int calc(int x,int y){ //count of words in ss[x..y]
int ans=;
for(int i=x;i<=y;++i){
int j,cur;
for(j=;j<s;++j){
for(cur=;w[j][cur];++cur)
if(i+cur>y||w[j][cur]!=ss[i+cur]) break;
if(w[j][cur]=='\0'){
++ans; break;
}
}
}
return ans;
}
int main(){
scanf("%d",&n);
for(int nn=;nn<n;++nn){
init();
for(int i=;i<=len;++i) f[i][]=calc(,i-);
for(int j=;j<=k;++j){
for(int i=j;i<=len;++i){
for(int x=;x<i-j;++x){
int tmp=calc(i-x,i-);
if(f[i][j]<f[i-x][j-]+tmp)
f[i][j]=f[i-x][j-]+tmp;
}
}
}
printf("%d\n",f[len][k]);
}
return ;
}

teacher's

2. codevs 1163 访问艺术馆--树形DP经典例题

1163 访问艺术馆

时间限制: 1 s

空间限制: 128000 KB

 题目等级 : 大师 Master
题目描述 Description

皮尔是一个出了名的盗画者,他经过数月的精心准备,打算到艺术馆盗画。艺术馆的结构,每条走廊要么分叉为二条走廊,要么通向一个展览室。皮尔知道每个展室里藏画的数量,并且他精确地测量了通过每条走廊的时间,由于经验老道,他拿下一副画需要5秒的时间。你的任务是设计一个程序,计算在警察赶来之前(警察到达时皮尔回到了入口也算),他最多能偷到多少幅画。

输入描述 Input Description

第1行是警察赶到得时间,以s为单位。第2行描述了艺术馆得结构,是一串非负整数,成对地出现:每一对得第一个数是走过一条走廊得时间,第2个数是它末端得藏画数量;如果第2个数是0,那么说明这条走廊分叉为两条另外得走廊。数据按照深度优先得次序给出,请看样例

输出描述 Output Description

输出偷到得画得数量

样例输入 Sample Input

60

7 0 8 0 3 1 14 2 10 0 12 4 6 2

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint

s<=600

走廊的数目<=100

题目分析:很明显,这是一棵二叉树,所以我们采用递归建树的方法。

DP方程:因为这是一棵树,所以不是线性循环,使用记忆化搜索是比较容易实现的。

对于每一个点:if这是叶节点,判断能拿到多少画

if这不是叶节点,就把当前的时间平分给左右结点,(从0--tt)循环,统计出最大值

/*首先,根据数据建立二叉树
定义f(tot,k)表示
在剩余时间是tot的情况下,出发去第k个结点为根的子树中,
能得到的画的最大数量。
时间tot的构成:
到达及离开子树的时间: 2*t[k].v
分别在左右子树中的时间: x y
*/
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define T 601
#define N 201
struct Node{
int tim,val,child[];
};
Node node[N];
int f[N][T];
int t,n=;
void build(int k)
{
++n;
int j=n;
scanf("%d%d",&node[n].tim,&node[n].val);
node[n].tim*=;/*计算上回去的时间,就直接把时间存成2倍*/
if(node[n].val)
{
node[n].child[]=node[n].child[]=;
return ;
}
node[j].child[]=n+;
build(n+);
node[j].child[]=n+;/*注意这里是node[j],储存在这个递归阶段的父节点,而不是n,因为n是全局变量,所以随时会变化*/
build(n+);
}
void input()
{
scanf("%d",&t);
int x,y;
build();
}
int DFS(int k,int ti)
{
int tt=ti;
if(f[k][tt]>) return f[k][tt];
ti-=node[k].tim;/*计算到达该点后当前的剩余时间*/
if(ti<=) return ;
if(node[k].val)/*如果是叶节点,就偷画*/
{
int c=;
int l=node[k].val;
while(ti>=&&l>=)/*注意这里不是node[k].val--,因为我们更新的时候,一个叶节点会被访问多次,这次减没了,下次来的时候就不对了*/
{
ti-=;
l--;//
c++;
}
return f[k][tt]=c;
}
for(int i=;i<=ti;++i)/*如果这不是叶节点,就把时间分为两份,一份去左孩子,一份去右孩子*/
{
int ans=;
ans+=DFS(node[k].child[],i);
ans+=DFS(node[k].child[],ti-i);
f[k][tt]=max(f[k][tt],ans);/*循环ti次更新出最大值,这也就是前面说的叶节点多次访问,不能把node[k].val--,否则会陷入死循环,一直return和进入*/
}
return f[k][tt];
}
int main()
{
input();
printf("%d\n", DFS(,t));
return ;
}

3.NOI 4982:踩方格

4982:踩方格--一个简单但是需要动脑子想方程的题

总时间限制: 
1000ms

内存限制: 
65536kB
描述

有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a.    每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b.    走过的格子立即塌陷无法再走第二次;
c.    只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。

输入
允许在方格上行走的步数n(n <= 20)
输出
计算出的方案数量
样例输入
2
样例输出
7
代码+分析:
/*这里采用倒推法:以后遇到没法直接寻找题目,或者题目条件不是很全,一般都是有很简单的方程,仅仅与n有关的方程*/
/*设l[i],r[i],u[i],设为最后一步向左,右,上走到第i个格子的方案数目,那么它的前一步,根据题目中说的“走过的格子立即塌陷无法再走第二次”,可以得出
l[i]=u[i-1]+l[i-1],r[i]=r[i-1]+u[i-1],u[i]=u[i-1]+l[i-1]+r[i-1],(可以看出u[i]=f[i-1]);
f[i]= u[i]+ l[i]+ r[i];
=2*(u[i-1]+r[i-1]+l[i-1])+u[i-1](代入上式)
所以f[i]=2*[i-1]+f[i-2]
*/
#include<cstdio>
long long int f[];
int n;
int main()
{
scanf("%d",&n);
f[]=;
f[]=;
for(int i=;i<=n;++i)
f[i]=*f[i-]+f[i-];
printf("%d",f[n]);
return ;
}

4.NOI 6252:带通配符的字符串匹配

6252:带通配符的字符串匹配

总时间限制: 
1000ms

内存限制: 
65536kB
描述

通配符是一类键盘字符,当我们不知道真正字符或者不想键入完整名字时,常常使用通配符代替一个或多个真正字符。通配符有问号(?)和星号(*)等,其中,“?”可以代替一个字符,而“*”可以代替零个或多个字符。

你的任务是,给出一个带有通配符的字符串和一个不带通配符的字符串,判断他们是否能够匹配。

例如,1?456 可以匹配 12456、13456、1a456,但是却不能够匹配23456、1aa456; 
2*77?8可以匹配 24457798、237708、27798。

输入
输入有两行,每行为一个不超过20个字符的字符串,第一行带通配符,第二行不带通配符
输出
如果两者可以匹配,就输出“matched”,否则输出“not matched”
样例输入
1*456?
11111114567
样例输出
matched
基本思路:
因为是两个字符创匹配的问题,所以定义状态为f[i][j]表示a串的前i个与b串的前j个是否匹配。
然后对a[i]的值进行讨论:

if(a[i]=='?')
f[i][j]=f[i-1][j-1];
else if(a[i]=='*')
f[i][j]=f[i-1][k](0<=k<=j)这里*比较特殊,如果a[i]=='*‘的话,那么只要f[i-1]与j及其之前的所有取值有一个f[i-1][k]匹配就可以了,因为*可以代表0--j所有的字符。为了减小循环次数,我们可以设一个ok变量来储存着结果
else f[i][j]=f[i-1][j-1]&&(a[i]==b[j]);

值得注意的一点:

注意a的前多项都是*的例子,那么这个时候f[i][0]就是true了,对于这种情况,我们必须考虑到

代码一:
#include<iostream>
using namespace std;
#include<cstdio>
char a[],b[],lena,lenb;
#include<cstring>
bool f[][];
int main()
{
scanf("%s%s",a+,b+);
lena=strlen(a+);
lenb=strlen(b+);
f[][]=true;
int l=;
while(a[l++]=='*')
{
f[l-][]=true;/*考虑前缀*的例子*/
}
for(int i=;i<=lena;++i)
{
bool ok=false;/*必须给ok初值是false,否则默认是true,会造成错误*/
for(int j=;j<=lenb;++j)
{
ok=ok||f[i-][]||f[i-][j];
if(a[i]=='?')
f[i][j]=f[i-][j-];
else if(a[i]=='*')
f[i][j]=ok;
else f[i][j]=f[i-][j-]&&(a[i]==b[j]);
}
}
if(f[lena][lenb]) printf("matched\n");
else printf("not matched\n");
return ;
}
代码二(思路是相同的):
#include<iostream>
using namespace std;
#include<cstdio>
#define N 25
#include<cstring>
int lena,lenb,f[N][N];
char a[N],b[N];
int main()
{
scanf("%s%s",a+,b+);
lena=strlen(a+);
lenb=strlen(b+);
f[][]=true;
for(int i=;i<=lena;++i)
{
bool ok=f[i-][];/*这里是处理前缀*的情况*/
if(a[i]=='*') f[i][]=ok;
for(int j=;j<=lenb;++j)
{
ok=ok||f[i-][j];/*因为ok本身已经有了f[i-1][0],所以不必再加了*/
if(a[i]=='?')
f[i][j]=f[i-][j-];
else if(a[i]=='*')
f[i][j]=ok;
else f[i][j]=f[i-][j-]&&(a[i]==b[j]);
}
}
if(f[lena][lenb]) printf("matched\n");
else printf("not matched\n");
return ;
}
5.洛谷P1282 多米诺骨牌

P1282 多米诺骨牌--01背包法

  • 标签动态规划
  • 难度提高+/省选-
  • 通过/提交282/964

题目描述

多米诺骨牌有上下2个方块组成,每个方块中有1~6个点。现有排成行的

上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|。例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置。

编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小。

对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0。

输入输出格式

输入格式:

输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数a和b,且1≤a,b≤6。

输出格式:

输出文件仅一行,包含一个整数。表示求得的最小旋转次数。

输入输出样例

输入样例#1:

4
6 1
1 5
1 3
1 2
输出样例#1:

1

问题分析:

很明显,对于当前骨牌只有翻与不翻两种选择,就像是01背包取物品的时候的取与不取是相通的,那么我们就可以尝试用01背包解决,如果这个牌不翻,会是什么状态?如果翻了是什么状态?选择的标准就是犯的次数最少。

代码一:

未压缩空间版:

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define INF 1010
#define Q 2000
int n,N,w[INF];
int f[INF][INF*];
void input()
{
int a,b;
scanf("%d",&n);
for(int i=;i<=n;++i)
{
scanf("%d%d",&a,&b);
w[i]=a-b;
}
}
void DP()
{
N=*n;/*进行平移的数,防止数组越界,5*n就是最大的差值了*/
memset(f,,sizeof(f));
f[][w[]+N]=;/*差值全部定义成a-b,那么对于前i张牌的状态,我们是可以直接得出的,这也是DP的边界*/
f[][-w[]+N]=;/**/
for(int i=;i<=n;++i)
for(int j=*n;j>=;--j)/*类似有01背包*/
{
if(j+w[i]>=&&j+w[i]<=*n)/*注意要下越界和上越界都判断,因为w[i]的正负是不一定的*/
f[i][j]=min(f[i][j],f[i-][j+w[i]]+);/*设t是前i-1个牌的某个翻法的差值推到f[i][j]这个状态,如果不翻牌,那么j=t+w[i],可以倒推出t的两个值,对应着翻牌与不翻牌*/
if(j-w[i]>=&&j-w[i]<=*n)
f[i][j]=min(f[i][j],f[i-][j-w[i]]);
}
if(f[n][*n]<Q) printf("%d\n",f[n][*n]);/*Q是我估计的最大翻转次数,这个用来判断当前的差值能不能通过翻牌得到,如果不能得到,一定比Q大,那么再向5*n的两侧找*/
else {
for(int i=*n-,j=*n+;j<=*n&&i>=;++j,--i)
{
if((f[n][i]<Q||f[n][j]<Q))
{
printf("%d\n",min(f[n][i],f[n][j]));
return ;
}
}
} }
int main()
{
input();
DP();
return ;
}
分析:
能否使用滚动数组,进行压缩空间?
答案是否定的。
让我们仔细看一下这个DP方程:
if(j+w[i]>=0&&j+w[i]<=10*n)
f[i][j]=min(f[i][j],f[i-1][j+w[i]]+1);
if(j-w[i]>=0&&j-w[i]<=10*n)
f[i][j]=min(f[i][j],f[i-1][j-w[i]]);
它的确符合只与上一层有关,但是遇上一层哪一个有关,就与01背包不同了,因为01背包倒序循环,
更新只与比二维j小的数有关,但是这个方程明显也可能与比二维j大的数有关,
所以不能用。

  

2016. 4.10 NOI codevs 动态规划练习的更多相关文章

  1. 2016.4.9 NOI codevs动态规划专练

    1.NOI 最大子矩阵 1:最大子矩阵 总时间限制:  1000ms 内存限制:  65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 ...

  2. 微信iphone7、 ios10播放视频解决方案 2016.11.10

    2016.11.10日更新以下方法 微信最新出同层播放规范 即使是官方的也无法解决所有android手机的问题. 另外iphone 5 .5s 某些手机始终会弹出播放,请继续采用 “以下是老的解决办法 ...

  3. 2016年10月31日 星期一 --出埃及记 Exodus 19:16

    2016年10月31日 星期一 --出埃及记 Exodus 19:16 On the morning of the third day there was thunder and lightning, ...

  4. 2016年10月30日 星期日 --出埃及记 Exodus 19:15

    2016年10月30日 星期日 --出埃及记 Exodus 19:15 Then he said to the people, "Prepare yourselves for the thi ...

  5. 2016年10月29日 星期六 --出埃及记 Exodus 19:14

    2016年10月29日 星期六 --出埃及记 Exodus 19:14 After Moses had gone down the mountain to the people, he consecr ...

  6. 2016年10月28日 星期五 --出埃及记 Exodus 19:13

    2016年10月28日 星期五 --出埃及记 Exodus 19:13 He shall surely be stoned or shot with arrows; not a hand is to ...

  7. 2016年10月27日 星期四 --出埃及记 Exodus 19:12

    2016年10月27日 星期四 --出埃及记 Exodus 19:12 Put limits for the people around the mountain and tell them, `Be ...

  8. 2016年10月26日 星期三 --出埃及记 Exodus 19:10-11

    2016年10月26日 星期三 --出埃及记 Exodus 19:10-11 And the LORD said to Moses, "Go to the people and consec ...

  9. 2016年10月25日 星期二 --出埃及记 Exodus 19:9

    2016年10月25日 星期二 --出埃及记 Exodus 19:9 The LORD said to Moses, "I am going to come to you in a dens ...

随机推荐

  1. Python中的subprocess模块

    Subprocess干嘛用的? subprocess模块是python从2.4版本开始引入的模块.主要用来取代 一些旧的模块方法,如os.system.os.spawn*.os.popen*.comm ...

  2. Coursera课程《Machine Learning》吴恩达课堂笔记

    强烈安利吴恩达老师的<Machine Learning>课程,讲得非常好懂,基本上算是无基础就可以学习的课程. 课程地址 强烈建议在线学习,而不是把视频下载下来看.视频中间可能会有一些问题 ...

  3. Sqlserver获取所有数据库名,表信息,字段信息,主键信息,以及表结构等。

    --获取所有数据库名: SELECT name FROM master..sysdatabases WHERE name NOT IN ( 'master', 'model', 'msdb', 'te ...

  4. sicily 1020. Big Integer

    Description Long long ago, there was a super computer that could deal with VeryLongIntegers(no VeryL ...

  5. 阿里云ftp连接遇到的错误,entering passive mode失败(一个并不成熟的产品?)

    ####禁止自行转载 ####违者必究 ####需要转载请联系我 因为最近用python写了一个自动上传的脚本,想通过FTP把数据上传到我的服务器. 于是, 兴高采烈地打开我一直信任的阿里云,yum ...

  6. Math.random易于记忆理解

    产生随机数 Math.random*(Max-Min)+Min

  7. mysql 库操作、存储引擎、表操作

    阅读目录 库操作 存储引擎 什么是存储引擎 mysql支持的存储引擎 如何使用存储引擎 表操作 创建表 查看表结构 修改表ALTER TABLE 复制表 删除表 数据类型 表完整性约束 回到顶部 一. ...

  8. Science14年的聚类论文——Clustering by fast search and find of density peaks

    欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 这是一个比较新的聚类方法(文章中没看见作者对其取名,在这里我姑且称该方法为local density clu ...

  9. LoadRunner中的C Vuser函数

    LoadRunner中的C Vuser函数     事务函数: lr_end_sub_transaction 标记子事务的结束以便进行性能分析. lr_end_transaction 标记事务的结束. ...

  10. MyBatis插入时候获取自增主键方法

    方法一:(Oralce不支持这种写法) useGeneratedkeys 默认值为 false,含义:设置是否使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty ...