pku2817 WordStack
题意:给定N 个字符串,每个字符串长度为小于等于10 ,要求得到一个排列,s[i] 为str[i] 和str[i - 1] 通过移动,相同位置上相同字符的个数,求max(s[1] + s[2] + s[3] +...)
分析:aaa 和 abc 的相同的字符数 为1
abc 和 adc 相同的字符数为2
cde 和 bfcde 相同的字符数为3
N个总数只有10, 很容易就想到了状态压缩,我们用dp[j][i]表示以第一个串结尾, 状态为j时 最大的相同字符数, j 的二进制位上每一个位的0 或1 表示是否已选了第几个串
pku2817
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<iterator> using namespace std; const int N = 10 + 5; int dp[1 << N][N]; char str[N][N]; int getNum(int u, int v) { int ret = 0; int len1 = strlen(str[u]), len2 = strlen(str[v]); for(int i = 0; i < len1; ++i) { int tmp = i, num = 0; for(int j = 0; j < len2 && tmp < len1; ++j) { if(str[u][tmp] == str[v][j]) { ++num; } ++tmp; } ret = max(ret, num); } return ret; } int dist[N][N]; int main() { int n; while(scanf("%d",&n) == 1) { if(n <= 0) break; memset(dp, -1, sizeof(dp)); for(int i = 0; i < n; ++i) { scanf("%s",str[i]); dp[(1 << i)][i] = 0; } for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) dist[i][j] = getNum(i, j); for(int i = 0; i < n; ++i) for(int j = i + 1; j < n; ++j) { int tmp = max(dist[i][j], dist[j][i]); dist[i][j] = dist[j][i] = tmp; } //for(int i = 0; i < n; ++i) //{ // for(int j = 0; j < n; ++j) // cout << dist[i][j] << ' '; // cout << endl; //} //cout << (1 << n) - 1 << endl; for(int m = 1; m < n; ++m) { for(int j = (1 << n) - 1; j >= 0; --j) { for(int k = 0; k < n; ++k) { if(dp[j][k] == -1) continue; for(int i = 0; i < n; ++i) { if( i == k) continue; if(j & ( 1 << i)) continue; int nex = j | (1 << i); //cout << nex << ' ' << j << endl; dp[nex][i] = max(dp[nex][i], dp[j][k] + dist[k][i]); //cout << dp[nex][i] << ' ' << dp[j][k] << ' ' << dist[k][i] << endl; } } } } int ans = 0; for(int i = 0; i < n; ++i) { ans = max(ans, dp[(1 << n) - 1][i]); } printf("%d\n", ans); } }
时间是一样的,不过少了一层for, 将枚举状态j的for循环倒过来,类似01背包和完全背包的那个过程
新版本
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<iterator> using namespace std; const int N = 10 + 5; int dp[1 << N][N]; char str[N][N]; int getNum(int u, int v) { int ret = 0; int len1 = strlen(str[u]), len2 = strlen(str[v]); for(int i = 0; i < len1; ++i) { int tmp = i, num = 0; for(int j = 0; j < len2 && tmp < len1; ++j) { if(str[u][tmp] == str[v][j]) { ++num; } ++tmp; } ret = max(ret, num); } return ret; } int dist[N][N]; int main() { int n; while(scanf("%d",&n) == 1) { if(n <= 0) break; memset(dp, -1, sizeof(dp)); for(int i = 0; i < n; ++i) { scanf("%s",str[i]); dp[(1 << i)][i] = 0; } for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) dist[i][j] = getNum(i, j); for(int i = 0; i < n; ++i) for(int j = i + 1; j < n; ++j) { int tmp = max(dist[i][j], dist[j][i]); dist[i][j] = dist[j][i] = tmp; } //for(int m = 1; m < n; ++m) //{ // for(int j = (1 << n) - 1; j >= 0; --j) // { // for(int k = 0; k < n; ++k) // { // if(dp[j][k] == -1) continue; // for(int i = 0; i < n; ++i) // { // if( i == k) continue; // if(j & ( 1 << i)) continue; // int nex = j | (1 << i); // dp[nex][i] = max(dp[nex][i], dp[j][k] + dist[k][i]); // // } // // } // } //} for(int j = 0; j <= (1 << n) - 1; ++j) { for(int k = 0; k < n; ++k) { if(dp[j][k] == -1) continue; for(int i = 0; i < n; ++i) { if( i == k) continue; if(j & ( 1 << i)) continue; int nex = j | (1 << i); dp[nex][i] = max(dp[nex][i], dp[j][k] + dist[k][i]); } } } int ans = 0; for(int i = 0; i < n; ++i) { ans = max(ans, dp[(1 << n) - 1][i]); } printf("%d\n", ans); } }