HDU3828 A + B problem

HDU3828 A + B problem

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 125536/65536 K (Java/Others)
Total Submission(s): 544    Accepted Submission(s): 116


Problem Description
QQ and OO always play the game of A + B. QQ gives tow decimal number A and B, and OO answers the sum at once. But to do the same things too often is boring. So, today they are fed up with the easy game, and they come up with new rules of the game.
Rule1: When add A and B we use the binary system string of A and B.
Rule2: We can overlap the same suffix of A and prefix of B.
Rule3: If the binary system string of B is a substring of A we can use A to overlap B.

To make the problem more interesting, QQ gives n numbers, OO should use every one of them and every one once, then give the smallest of the sum. Now OO have no time to do it and need your help. You're a talented programmer you can do it.
 
Input
There are several tests, every test begin with n followed with n (0 < n < 16) lines, each line is a decimal number ai(0 < ai < 2^64) as described above.
Process until EOF.
 
Output
For each test you should print the smallest answer per line, maybe the answer is large you should mod 1000000009.
 
Sample Input
2
5
3
3
245
351
107
Sample Output
11
3935
********************************************************************
题目大意:有n个数要求连起来。首先这些数字先都写成二进制,然后,对于两个数a和b,如果二进制中a是b的子串,那么连起来的话就是b了,即b把a覆盖了。如果a和b不是子串关系,如果二进制串中,a的末尾几个数和b的初始几个数相同,那么a和b连起来就成了a和b连着写,但是中间一样的东西只写一遍。现在要把n个数连起来,求连起来之后的串转化成10进制最小的值。
解体思路:对于一个二进制串转化成十进制最小有两个条件:1,这个串的长度最小;2,这个串是最小字典序。
原本想了很多dp思路,但是碍于庞大的数据量,或者是数组不好开,或者是dp内部不知道放什么,实在是没法子了,参考了一下网上的资料,唉,不爽啊,这道dp题又算是白做了。
网上说,dp是二维dp,第一维存的是状态,第二维存的是总字符串最左边开始的那个字符串的序号,dp里面存的是当前最短的总字符串长度值。我就纳闷了,如果这个dp只是求把这几个二进制串连在一起的最短的值,那很好理解,但现在要求的是最小的字典序的串,这个状态怎么能保证最优呢?然而是我才疏学浅了,这样的确可以求出最优值。dp首先把最短的字符串的长度算出来了,然后我们由最后的dp再倒着往前推,我们按字段序最小往前推,这样就能算出最小值!太犀利了。
还有一点,字典序最小往前推的时候并不是数字的字典序,而是数字的部分字典序,我想我讲的很乱,唉,语文没学好,就这样吧。
#include <stdio.h>
#include <string.h>
#include <string>
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <algorithm>
#include <iostream>
#define N 16
#define M 1<<16
#define MS 1000000009
#define LL long long
using namespace std;

int n,goal;
string str[N];
LL da[N];
int gra[N][N],mark[N];
int slen[N];
int dp[M][N];
LL ans;

void ini(void)
{
    goal=(1<<n)-1;
    for(int i=0;i<n;i++)
        str[i].clear();
    for(int i=0;i<n;i++)
    {
        LL k=da[i];
        while(k)
        {
            str[i].push_back((k&1)+'0');
            k=k>>1;
        }
        int j=str[i].size();
        string tempstr;
        while(j--)
            tempstr.push_back(str[i][j]);
        str[i]=tempstr;
    }
    sort(str,str+n);
    for(int i=0;i<n;i++)
        slen[i]=str[i].size();
    memset(mark,0,sizeof(mark));
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(i!=j&&slen[i]>slen[j])
                for(int k1=0;k1<slen[i]-slen[j]+1;k1++)
                {
                    int flag=0;
                    for(int k2=0;k2<slen[j];k2++)
                        if(str[i][k1+k2]!=str[j][k2])
                        {
                            flag=1;
                            break;
                        }
                    if(flag==0)
                    {
                        if(!mark[j])
                        {
                            mark[j]=1;
                            goal-=(1<<j);
                        }
                        break;
                    }
                }
    for(int i=0;i<n-1;i++)
        for(int j=i+1;j<n;j++)
            if(!mark[i]&&!mark[j]&&str[i]==str[j])
                mark[j]=1,goal-=(1<<j);
    memset(gra,0,sizeof(gra));
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(!mark[i]&&!mark[j]&&i!=j)
                for(int k=0;k<slen[i];k++)
                {
                    int flag=0;
                    for(int u=0;u+k<slen[i];u++)
                        if(str[i][k+u]!=str[j][u])
                        {
                            flag=1;
                            break;
                        }
                    if(!flag)
                    {
                        gra[i][j]=slen[i]-k;
                        break;
                    }
                }
}

void dfs(int s,int p)
{
    if(~dp[s][p])return ;
    if(s==(1<<p))
    {
        dp[s][p]=slen[p];
        return ;
    }
    int minn=0x3f3f3f3f;
    int t=s-(1<<p);
    for(int i=0;i<n;i++)
        if(!mark[i]&&i!=p&&(s&(1<<i)))
        {
            dfs(t,i);
            minn=min(minn,dp[t][i]-gra[p][i]);
        }
    dp[s][p]=minn+slen[p];
}

void rans(int s,int p,int e)
{
    for(int i=e;i<slen[p];i++)
        ans=(ans*2+str[p][i]-'0')%MS;
    if(s==(1<<p))return ;
    int t=s-(1<<p);
    int k=-1;
    string a;
    for(int i=0;i<n;i++)
        if(!mark[i]&&(s&(1<<i))&&dp[t][i]-gra[p][i]+slen[p]==dp[s][p])
        {
            string b(str[i],gra[p][i],slen[p]-gra[p][i]);
            if(k==-1||a>b)
                a=b,k=i;
        }
    rans(t,k,gra[p][k]);
}

void re(void)
{
    for(int i=0;i<n;i++)
        scanf("%lld",&da[i]);
}

void run(void)
{
    ini();
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<n;i++)
        if(!mark[i])
            dfs(goal,i);
    int p,minn=0x3f3f3f3f;
    for(int i=0;i<n;i++)
        if(!mark[i]&&dp[goal][i]<minn)
            p=i,minn=dp[goal][i];
    ans=0;
    rans(goal,p,0);
    printf("%lld\n",ans);
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        re();
        run();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Fatedayt/p/2266818.html