HDU 4295 狀態壓縮dp + KMP

鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=4295

題意:給你一個字符串和他的4個子串,將這4個子串放到原串裏面(可以重疊),問你最多能覆蓋多少個字符,最少能覆蓋多少個字符

解析:網賽的時候DP還是太水了,居然不敢想這個題目,現在看下,其實這個題目還是不難的,只是不好寫,用KMP預處理子串可以插入的位置,dp【i】【j】【k】表示位置i,4個字符串的使用狀態爲j,k表示 從i往後已經覆蓋了K個了,狀態出來了,轉移應該不難,注意:一個位置可以放多個串,這是難點,不過經過預處理就好了,一次性在某個位置放很多個串。

代碼寫的很挫,我以爲預處理了這麼多,應該蠻快的,沒想到卡時間過的,看解析夠了。。。代碼慘不忍睹


#include<stdio.h>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 4100;
const int maxm = 70;
int dp[maxn][20][maxm][2],ok[4][maxn],next[maxm],n,m,l[4],son[4][4];
char s[maxn],p[4][maxm];
vector<int> h[maxn];
void set_next(int id);
void kmp(int id);
void init();
void solve();
int main()
{
    while(scanf("%s",s) != EOF)
    {
        n = strlen(s) , m = 0;
        for(int i = 0; i < 4; i ++) scanf("%s",p[i]),l[i] = strlen(p[i]),m = max(m,l[i]);
        init();
        solve();
    }
    return 0;
}
void init()
{
    memset(ok,false,sizeof(ok));
    memset(son,false,sizeof(son));
    for(int i = 0; i < 4; i ++) set_next(i),kmp(i);
    for(int i = 0; i < 4; i ++)
        for(int j = 0; j < 4; j++)
        {
            if(i == j) continue;
            if(l[j] > l[i]) continue;
            if(strncmp(p[i],p[j],l[j]) == 0) son[i][j] = true;
        }
    for(int i = 0; i < 4; i ++)
    {
        son[i][i] = true;
        h[i].clear();
        for(int j = 0,k; j < 16; j ++)
        {
            if(j & (1 << i));
            else continue;
            for(k = 0; k < 4; k ++)
                if((j & (1 << k)) && son[i][k] == 0) break;
            if(k >= 4) h[i].push_back(j);
        }
    }
    // for(int i = 0; i < 4; i++) for(int j = 0; j < h[i].size(); j ++) printf("I:%d   h:%d\n",i,h[i][j]);
}
void set_next(int id)
{
    int j = 0,k = -1,len = strlen(p[id]);
    next[j] = k;
    while(j < len)
    {
        if(k == -1 |p[id][j] == p[id][k]) j ++,k ++,next[j] = k;
        else k = next[k];
    }
}
void kmp(int id)
{
    int i,j,len = strlen(p[id]);
    i = j = 0;
    while(i < n)
    {
        if(j == -1 | s[i] == p[id][j]) i ++,j ++;
        else j = next[j];
        if(j == len) ok[id][i - j] = true;
    }
    if(j == len) ok[id][i - j] = true;
}
void solve()
{
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < 16; j ++)
            for(int k = 0; k <= m; k ++)
                dp[i][j][k][0] = maxn,dp[i][j][k][1] = 0;
    dp[0][0][0][0] = dp[0][0][0][1] = 0;
    for(int i = 0; i < 4; i ++)
        if(ok[i][0]) dp[0][1 << i][l[i]][0] = dp[0][1 << i][l[i]][1] = l[i];
    for(int i = 1; i < n; i ++) //position
    {
        for(int j = 0; j < 16; j ++)
        {
            for(int k = 0; k < m; k ++)
                dp[i][j][k][0] = dp[i - 1][j][k + 1][0],dp[i][j][k][1] = dp[i - 1][j][k + 1][1]; //not insert
            dp[i][j][0][0] = min(dp[i][j][0][0],dp[i - 1][j][0][0]);
            dp[i][j][0][1] = max(dp[i][j][0][1],dp[i - 1][j][0][1]);
        }

        for(int j = 0; j < 16; j ++) //used?
        {
            for(int k = 0; k <= m; k ++) //have benn covered
            {
                for(int v = 0; v < 4; v ++)
                {
                    int a = j & (1 << v);
                    if(!a && ok[v][i])
                    {
                        int co = l[v],nco;
                        nco = max(k - 1,co);
                        if(nco > k - 1 && k) co = nco - k + 1;
                        else co = 0;
                        if(!k) co = l[v];
                        for(int tot = 0; tot < h[v].size(); tot ++)
                        {
                            dp[i][j | h[v][tot]][nco][0] = min(dp[i][j | h[v][tot]][nco][0],dp[i - 1][j][k][0] + co);
                            dp[i][j | h[v][tot]][nco][1] = max(dp[i][j | h[v][tot]][nco][1],dp[i - 1][j][k][1] + co);
                        }

                    }
                }
            }
        }
    }
    int mx = 0,mn = maxn;
    for(int k = 0; k < m; k ++) mx = max(mx,dp[n - 1][15][k][1]),mn = min(mn,dp[n - 1][15][k][0]);
    printf("%d %d\n",mn,mx);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章