HDU 3828 A+B problem

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.

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.


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


#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了。。
注意,相交可以预处理出来,集合要反复用时,最好也先预处理出来
*****************/

你可能感兴趣的:(HDU 3828 A+B problem)