题目链接:https://www.nowcoder.com/acm/contest/116/J
有n个原子,任意两个原子互相撞击会产生一定的能量,并且被撞击的那个会消失,然后要你求当n个原子发生了n-1次撞击后能产生的最大能量.
输入:包含多组实例.每个实例的第一行是N(2<=N<=10),然后接下来N行,每行有N个整数x(0<=x<=1000),第i行的第j个数,表示i原子撞击j原子后产生的能量,且j消失.当N为0时表输入结束.
输出:对于每个实例输出能量最大值.
分析:本题和TSP问题很相似,使用类似的状态定义方式解即可.
令d[i][S]表示当前在原子i且已经用过的原子集合为S(S包括i)时,能产生的最大能量.
状态转移方程是:d[i][S|(1<<i)]=max{ d[j][S]+v[i][j] },用i去撞j,或d[j][S|(1<<i)]=max{d[j][S]+v[j][i] },用j去撞i
初值:d[i][1<<i]=0.
这么做此题就是错的.题意理解有偏差:本题的意思是任意两个原子都可以相撞,并不一定是要依次相撞.比如6个原子,现在123原子已经撞了剩下3号原子,接下来我可以用5和6撞,然后再用剩下的去撞3,而并不一定需要用3去撞{4,5,6}中的一个.所以正确的解法应该是:
令d[S]=x表示当前集合S中表示的原子已经销毁了,能产生的最大能量值.
d[S+{j}]=max{ d[S]+v[i][j] },i与j都是集合S外的原子且用i撞j.
d[S+{i}]=max{ d[S]+v[j][i] },i与j都是集合S外的原子且用j撞i.
代码:
#include <bits/stdc++.h> using namespace std; const vector<int> v {1,2,4,8,16,32,64,128,256,512,1024,2048}; int main() { int n; while(scanf("%d", &n) && n) { vector<vector<int> >e (n, vector<int>(n)); for(int i = 0;i < n;i ++) for(int j = 0;j < n;j ++) scanf("%d", &e[i][j]); vector<int>dp(v[n], 0); for(int i = 0;i < v[n];i ++) { for(int j = 0;j < n;j ++) { if(i & v[j]) continue; for(int k = 0;k < n;k ++) { if(k != j && !( i & v[k] ) ) { dp[i | v[j]] = max(dp[i | v[j]], dp[i] + e[k][j]); } } } } int res = 0; for(int i = 0;i < v[n];i ++) res = max(res, dp[i]); printf("%d\n", res); } return 0; }