hdu3828动态规划

 题目描述:
题意就不讲了,最终的模型是:给你n个长度64的串,让你找一个最短的串str,让这个n个串都是str的子串,如果长度相同,取字典序最小的。
解题报告:
首先,预处理,share[i][j]表示第i个字符串的尾部和第j个字符串的头部的公共长度,如果j是i的子串,直接删除j。
我们从右往左构造字符串(应为比较字典序是从往右的,所以当有答案冲突时,只需要比较最左端的字符串即可得到字典序最小的答案)。
dp[i][j]表示当前在i状态时,用j串作为最左端的串时的最小长度。
i是一个15位二进制数字,哪一位为1表示那一位对应的字符串已经使用过了。
那么,从dp[i][j]开始,一次扫描n个串:
对于串k,如果(i>>k&1) == 0,表示还没用过k,那么可以转移到dp[(i | (1 << k))][k]
转移后的最小长度为:dp[i][j] + len[k] - share[k][j];(减去公共的部分)
这样,每个dp[i][j]都维持一个最小的数字,最后扫描dp[1 << n - 1][x]中的最小值即可。

对于冲突,要维护一个pre[i][j],表示dp[i][j]是由哪个状态转化过来的。如果在dp[i][j],冲突,由于是从右往左拼接,那么只需比较冲突的两个串的最头的两个的字典序(比较绕,应该能明白)。

 

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define NN 66000
#define N 25
#define inf (1 << 30)
#define mod 1000000009
using namespace std;

int n;
int fail[100], able[20], len[20];
int share[20][20], dp[85536][20], pre[85536][20][2];
long long a[20];
char str[20][100], cat[20][20][200], tmp[100], ans_s[1100];
vector<int> q;

/*==================================================*\
| KMP匹配算法O(M+N)
| CALL: res=kmp(str, pat); 原串为str; 模式为pat(长为P);
\*==================================================*/
int kmp(char* str, char* pat)
{
    int i, j, k;
    memset(fail, -1, sizeof(fail));
    for (i = 1; pat[i]; ++i)
    {
        for (k = fail[i-1]; k >= 0 && pat[i] != pat[k + 1];
                k = fail[k]);
        if (pat[k + 1] == pat[i]) fail[i] = k + 1;
    }
    i = j = 0;
    while( str[i] && pat[j] )  // By Fandywang
    {
        if( pat[j] == str[i] ) ++i, ++j;
        else if (j == 0) ++i;//第一个字符匹配失败,从str下个字符开始
        else j = fail[j - 1] + 1;
    }
    if (pat[j]) return j;
    else return -1;
}

int init()
{
    int i, j, size, e;
    long long temp;
    for (i = 1; i <= n; i++)
    {
        temp = a[i];
        size = 0;
        while (temp != 0)
        {
            if (temp % 2) tmp[size++] = '1';
            else tmp[size++] = '0';
            temp /= 2;
        }
        for (j = 0; j < size; j++)
            str[i][j] = tmp[size - 1 - j];
        str[i][size] = 0;
    }
    fill(able + 1, able + n + 1, 1);
    for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
        {
            if (j == i || able[j] == 0) continue;
            if (kmp(str[j], str[i]) == -1) able[i] = 0;
        }
    e = 0;
    for (i = 1; i <= n; i++)
        if (able[i]) strcpy(str[++e], str[i]);
    n = e;
    for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
        {
            len[i] = strlen(str[i]);
            share[i][j] = kmp(str[i], str[j]);
        }
    for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
        {
            if (j == i) continue;
            strcpy(cat[i][j], str[i]);
            strcat(cat[i][j], str[j] + share[i][j]);
        }
    memset(dp, -1, sizeof(dp));
    memset(pre, -1, sizeof(pre));
    for (i = 1; i <= n; i++)
    {
        dp[1 << (i - 1)][i] = len[i];
        pre[1 << (i - 1)][i][0] = -1;
    }
    return 1;
}

int get_ans(int k, int id, int from)
{
    if (from == -1) strcpy(ans_s, str[id]);
    else strcat(ans_s, str[id] + share[from][id]);
    if (pre[k][id][0] != -1 && pre[k][id][1] !=- 1)
        get_ans(pre[k][id][0], pre[k][id][1], id);
    return 1;
}

int get_int()
{
    int i, size = strlen(ans_s);
    long long ans = 0, temp = 1;
    for (i = size - 1; i >= 0; i--)
    {
        if (ans_s[i] == '1')
        {
            ans += temp;
            if (ans > mod) ans %= mod;
        }
        temp *= 2;
        if (temp > mod) temp %= mod;
    }
    printf("%I64d\n", ans);
    return 1;
}

int solve()
{
    int i, j, k, st, val, id ,maxi;
    for (i = 1; i < (1 << n); i++)
        for (j = 1; j <= n; j++)
        {
            if (dp[i][j] == -1) continue;
            for (k = 1; k <= n; k++)
            {
                if ((1 << (k - 1)) & i) continue;
                st = i | (1 << (k - 1));
                val = dp[i][j] + len[k] - share[k][j];
                if (dp[st][k] == -1 || dp[st][k] > val)
                {
                    dp[st][k] = val;
                    pre[st][k][0] = i;
                    pre[st][k][1] = j;
                }
                else if (dp[st][k] == val)
                {
                    if (strcmp(cat[k][j], cat[k][pre[st][k][1]]) < 0)
                        pre[st][k][0] = i, pre[st][k][1] = j;
                }
            }
        }
    k = (1 << n) - 1, id = -1, maxi = inf;
    for (i = 1; i <= n; i++)
    {
        if (dp[k][i] < maxi) maxi = dp[k][i], id = i;
        else if (dp[k][i] == maxi && strcmp(str[i], str[id]) < 0) id = i;
    }
    memset(ans_s, 0, sizeof(ans_s));
    get_ans(k, id, -1);
    get_int();
    return 1;
}

int main()
{
    int i, j;
    while (scanf("%d", &n) != EOF)
    {
        for (i = 1; i <= n; i++) scanf("%I64d", &a[i]);
        init();
        solve();
    }
    system("pause");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值