poj2817(N!的状压DP)

题意:将n个长度最长为10的字符串排列n行,上下对齐时候可以任意平移每一行,将每一行与上一行对齐位置相同的字符数加起来,问最多总共能有多少个。将最终数量输出。

例:

5 
abc 
bcd 
cde 
aaa 
bfcde 
的结果展示如下:
aaa 

abc 

 bcd

  cde 

bfcde

解法:方法是状态压缩记忆化搜索。以前做的状态压缩都是对于解集是2^n数量级形式的,做这道题最大的收获就是知道了对于N!数量级形式的问题如何进行状压了。在这道题中,首先求出两两字符串之间的最大匹配数,结果放在num[i][j]二维数组中,用于以后O(1)调用。状压时sum的每个二进制位的0和1分别表示相应字符串在与不在这个集合中。ans[sum][bit]表示在sum表示的字符串集合中以第bit的那个字符串结束的最大值(第bit字符串一定是在sum集合中的)。转移的时候就是枚举倒数第二结尾字符串了。祥见代码。不过这题由于数据太小,完全暴力枚举10!的排列也是可行的,感觉题的数据出的不太合理,如果给到20左右卡掉暴力就好了。后面附上暴力过的代码。状压0ms,暴力300+ms。

状压代码:

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <string.h>
using namespace std;
char s[10][11];
char num[10][10];
char ans[1024][10];
int n;
void help()
{
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
    {
        int len1=strlen(s[i]);
        int len2=strlen(s[j]);
        for(int k=0;k<len1;k++)
        {
            int tool=0;
            for(int h=0;h+k<len1&&h<len2;h++)
                if(s[i][k+h]==s[j][h])
                tool++;
            num[i][j]=max(int(num[i][j]),tool);
        }
        for(int k=0;k<len2;k++)
        {
            int tool=0;
            for(int h=0;h+k<len2&&h<len1;h++)
             if(s[i][h]==s[j][h+k])
                tool++;
            num[i][j]=max(int(num[i][j]),tool);
        }
        num[j][i]=num[i][j];
    }
}
int find(int sum,int bit)
{
    if(ans[sum][bit]!=-1)
        return ans[sum][bit];
    int tool=0;
    for(int i=0;i<n;i++){
        if(i!=bit&&(sum&(1<<i))){
            tool=max(tool,find(sum-(1<<bit),i)+num[bit][i]);
        }
    }
    return ans[sum][bit]=tool;
}
int main()
{
    while(scanf("%d",&n)==1)
    {
        if(n==0)
            break;
    memset(ans,-1,sizeof ans);
    memset(num,0,sizeof num);
    for(int i=0;i<n;i++)
      scanf("%s",s[i]);
        help();
    for(int i=0;i<n;i++)
        ans[(1<<i)][i]=0;
    int tool=0;
    int t=(1<<n)-1;
    for(int i=0;i<n;i++){
        tool=max(tool,find(t,i));
    }
    printf("%d\n",tool);
    }
    return 0;
}

暴力代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <string.h>
#include <algorithm>
using namespace std;
char s[12][12];
int num[12][12];
int ans[1200][12];
int n;
void help()
{
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
    {
        int len1=strlen(s[i]);
        int len2=strlen(s[j]);
        for(int k=0;k<len1;k++)
        {
            int tool=0;
            for(int h=0;h+k<len1&&h<len2;h++)
                if(s[i][k+h]==s[j][h])
                tool++;
            num[i][j]=max(num[i][j],tool);
        }
        for(int k=0;k<len2;k++)
        {
            int tool=0;
            for(int h=0;h+k<len2&&h<len1;h++)
             if(s[i][h]==s[j][h+k])
                tool++;
            num[i][j]=max(num[i][j],tool);
        }
        num[j][i]=num[i][j];
    }
}
int arrange[12];
int main()
{
    while(scanf("%d",&n)==1)
    {
        if(n==0)
            break;
    memset(ans,-1,sizeof ans);
    memset(num,0,sizeof num);
    for(int i=0;i<n;i++)
      scanf("%s",s[i]),arrange[i]=i;
        help();
    int tool=0;
       for(int i=0;i<n-1;i++)
        tool+=num[arrange[i]][arrange[i+1]];
    while(next_permutation(arrange+0,arrange+n))
    {
        int t=0;
        for(int i=0;i<n-1;i++)
        t+=num[arrange[i]][arrange[i+1]];
        tool=max(tool,t);
    }
    cout<<tool<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值