Fork me on GitHub

P1206 统计单词个数dp

https://www.luogu.org/problem/show?pid=1026#sub
点:同一字母不能重复用做首字母。
首先,处理出数组w[i][j] ,表示从i到j有多少个单词(当以同一个字母为首字母时,越短越好)。
然后,求出d[i],表示以i开头的最短单词的结尾的位置。
数组f[i][j]表示以i为终点,分为j份最多单词数。
然后状态转移方程:
f[i][j]=max(f[i][j],f[k][j-1]+w[k+1][i]) (j-1<=k<=i-1)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int w[209][209],n,m,s;
int a[209],d[209],f[209][209];
struct word{
    int l;
    int c[201];
}b[10];
int comp(const word&x,const word&y)
{
    return x.l<y.l?1:0;
}
int main()
{
    scanf("%d%d",&n,&m);    
    for(int i=1;i<=20*n;i++)
    {
        char c;
        cin>>c;
        a[i]=c-'a'+1;
    }
    scanf("%d",&s);
    for(int i=1;i<=s;i++)
    {
        char c[201];
        cin>>c;
        for(int j=0;j<strlen(c);j++)
          b[i].c[j+1]=c[j]-'a'+1;
        b[i].l=strlen(c);   
    }
    sort(b+1,b+s+1,comp);

    for(int i=1;i<=20*n;i++)
    {
        for(int j=1;j<=s;j++)
        {
            int p=0;
            for(int k=1;k<=b[j].l;k++)
            {
                int t=i+k-1;
                if(a[t]!=b[j].c[k])
                 p=1;
            }
            if(!p)
            {d[i]=i+b[j].l-1;
             break;}
        }
    }
    for(int i=1;i<=20*n;i++)
    {
        for(int j=i;j<=20*n;j++)
        {
            for(int p=i;p<=j;p++)
             if(d[p]<=j&&d[p]) w[i][j]++;
        }
    }
    for(int i=1;i<=20*n;i++)
     for(int j=1;j<=m;j++){
        for(int k=j-1;k<=i-1;k++)
         f[i][j]=max(f[i][j],f[k][j-1]+w[k+1][i]);
     }
    printf("%d",f[20*n][m]);
    return 0;
}
posted @ 2017-09-24 17:48  primes  阅读(83)  评论(0编辑  收藏  举报