245 11110101
351 101011111
107 1101011
+-------------------------
3935 111101011111
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.3935
#include <stdio.h> #include <stdlib.h> #include <string.h> int n; int lena[16]; char a[16][65]; int f[1 << 16][16]; // f[i][j] 状态i,以j为开头时,串最小长 int match[16][16]; //k = match[i][j], a[i][len - k..0] == a[j] [len..k] int DtoB(long long d, char *b){ int i; for (i = 0; d > 0; i++){ b[i] = d & 1; d = d >> 1; } return i; } int Cmp(int p, int q, int pre, int stat){ int i, j; i = f[stat][p]; j = f[stat][q]; if (pre >= 0){ i = i - match[pre][p]; j = j - match[pre][q]; } if (i > j) return 1; if (i < j) return -1; i = lena[p] - 1; j = lena[q] - 1; if (pre >= 0){ i = i - match[pre][p]; j = j - match[pre][q]; } for (; i >= 0 && j >= 0; i--, j--) if (a[p][i] != a[q][j]) break; if (i < 0 || j < 0) return 0; if (a[p][i] > a[q][j]) return 1; else return -1; } void CalcMatch(){ int i, j, p, q; for (i = 0; i < n; i++) for (j = 0; j < n; j++){ if (i == j) continue; for (p = lena[i] - 1; p >= 0; p--){ for (q = lena[j] - 1; q >= 0 && p - (lena[j] - 1 - q) >= 0; q--) if (a[i][p - (lena[j] - 1 - q)] != a[j][q]) break; if (p - (lena[j] - 1 - q) < 0 || q < 0) break; } match[i][j] = lena[j] - q - 1; } } int main(){ int i, j, k, i1, lent, pre, fstat; long long t; char ans[1024]; // freopen("A+B.in","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d", &n) != EOF){ fstat = (1 << n) - 1; for (i = 0; i < n; i++){ scanf("%I64d", &t); lena[i] = DtoB(t, a[i]); } CalcMatch(); memset(f[0], 0, sizeof(f[0])); for (i = 1; i <= fstat; i++){ for (k = 0; k < n; k++){ if (((1 << k) & i) == 0) continue; i1 = i - (1 << k); if (i1 == 0){ f[i][k] = lena[k]; continue; } f[i][k] = 0x7fffffff; for (j = 0; j < n; j++){ if (((1 << j) & i1) == 0) continue; if (f[i][k] > f[i1][j] + lena [k] - match[k][j]) f[i][k] = f[i1][j] + lena[k] - match[k][j]; } } } pre = -1; lent = 0; while(fstat){ k = -1; for (i = 0; i < n; i++){ if (((1 << i) & fstat) == 0) continue; if (k < 0 || Cmp(i, k, pre, fstat) <= 0) k = i; } fstat = fstat - (1 << k); if (pre >= 0) i = lena[k] - 1 - match[pre][k]; else i = lena[k] - 1; if (i >= 0) pre = k; for (; i >= 0; i--, lent++) ans[lent] = a[k][i]; } t = 0; for (i = 0; i < lent; i++){ t = (t * 2 + ans[i]) % 1000000009; } printf("%I64d\n", t); } return 0; } /**************** 一看到n<16,就想到TSP问题,集合DP f[i][j] 状态i,以j为开头时,串最小长 先把最小串长求出来,之后再把串搜出来。 由于是以j为开头,所以当两个状态有相同长度时,取串j字典序小的 这样保证了得到结果最小。 之所以不直接记录串本身,只记录串长,是觉得貌似不满足最优子结构性质,即 原串字典序最小,子串字典序不一定也最小的感觉。。。其实也看怎么设计了。 。。反正这么写过一次WA了。。 注意,相交可以预处理出来,集合要反复用时,最好也先预处理出来 *****************/