题目:http://poj.org/problem?id=1722
Time Limit: 1000MS | Memory Limit: 10000K | |||
Total Submissions: 1696 | Accepted: 746 | Special Judge |
Description
con([12,10,4,3,5],2) = [12,6,3,5] con([12,6,3,5] ,3) = [12,6,-2] con([12,6,-2] ,2) = [12,8] con([12,8] ,1) = [4]
Input
Output
Sample Input
5 4 12 10 4 3 5
Sample Output
2 3 2 1分析:很经典的dp题,开始真心想不出(甚至还往枚举搜索上想过||-_-,原来这是dp,现在发现动态规划在时间和空间上的好处真是巨大而明显)。
/* 不断的作差,改变差顺序可以使得整个序列得到t。由于顺序的改变可能使得'-'变成'+', 比如12-(10-4-(3-5))=12-(10-4-3)-5=12-10+4+3-5. 于是问题就变成了n个数字的加减组合得到t。即a[1]-a[2]±a[3]±a[4]±...±a[n]=t. 进一步转化:±a[3]±a[4]±...±a[n]=t-a[1]+a[2]. 背包问题!--》 2a[3]+2[4]+...+2a[n]=t-a[1]+a[2]+(a[3]+[4]+...+a[n]); 2a[i]可选可不选,不选对应减 */ #include <cstdio> #include <cstring> #include <cstring> #define V 30000 #define N 200 int number[N]; int dp[V]; int record[N][V]; bool tag[N]; int sta[N]; int main() { //freopen("cin.txt","r",stdin); int n,t; while (~scanf("%d%d", &n, &t)) { int a, b; if (n == 1) { scanf("%d", &a); continue; } if (n == 2) { scanf("%d%d", &a, &b); printf("%d\n", 1); continue; } scanf("%d%d", &a, &b); int sum = 0; for (int i = 0; i < n-2; i++) { scanf("%d", &number[i]); sum += number[i]; number[i] *= 2; } int v = sum-a+b+t; memset(dp,0,sizeof(dp)); for (int i = 0; i < n-2;i++) { //物件数 for (int j = v; j - number[i] >= 0; j--) { if (dp[j] > dp[j-number[i]]+number[i]) record[i][j] = 0; //不取 对应着‘-’ else { dp[j] = dp[j-number[i]]+number[i]; record[i][j] = 1; //取 ‘+’ } } } //printf("dp = %d(v= %d)\n", dp[v], v); int nowv = v; for (int i = n-2-1; i >= 0; i--) { if (record[i][nowv] == 1) { tag[i] = 1; nowv -= number[i]; //等式两边减去加上的数字 } else tag[i] = 0; } int top = 0; for (int i = 0; i < n-2; i++) { if (tag[i] == 1) sta[top++] = i+2; //dex } for (int i = 0; i < top; i++) sta[i] -= i; //很重要,i其实也就是加号个数-1 for (int i = 0; i < top; i++) printf("%d\n", sta[i]); for (int i = top; i < n-1; i++) puts("1"); } return 0; }