2018年信息学奥赛NOIP资料下载
题目链接:https://www.luogu.org/problemnew/solution/P1026
思路:暴力预处理区间[l,r]的单词数,l,r∈[1,len]\wedgel<=r,然后dp[i][k]表示到第i个位置分成k块的最大单词数
转移方程:dp[i][k]=max(dp[i][k],dp[j][k-1]+num[j+1][1]) j∈[1,i]
参考代码:
#include<bits/stdc++.h>
using namespace std;
int m;
string s,dic[10],ch;
int num[205][205],dp[205][45];
bool judge(int l,int r)
{
string ss=s.substr(l,r-l+1);//取出l到r的字符串
for(int i=1;i<=m;i++)
if(ss.find(dic[i])==0)return true;//find返回的是第一个下标
return false;
}
int main()
{
ios::sync_with_stdio(false);
int p,k;
cin>>p>>k;
s+='\0';//为了s的下标从1开始
for(int i=1;i<=p;i++)
{
cin>>ch;
s+=ch;
}
cin>>m;
for(int i=1;i<=m;i++)
cin>>dic[i];
int len=s.length()-1;
for(int i=len;i>=1;i--)//预处理单词数,i为右端点,j为左端点
{
for(int j=i;j>=1;j--)
{
num[j][i]=num[j+1][i];
if(judge(j,i))num[j][i]++;
}
}
dp[0][0]=0;
for(int i=1;i<=len;i++)//边界状态,到i只分成一块即1-i的单词数
dp[i][1]=num[1][i];
for(int i=1;i<=len;i++)//枚举右端点,左端点都是max(1,分的段数)
for(int j=1;j<=k&&j<=i;j++)//枚举分成了几段
for(int l=j;l<i;l++)//枚举左端点与右端点之间的点
dp[i][j]=max(dp[i][j],dp[l][j-1]+num[l+1][i]);
printf("%d\n",dp[len][k]);
return 0;
}