这题一开始没有什么想法,后来只能上网看别人的解题报告,看到了状态压缩DP这种做法,好神奇。。。
对于有n个原子,在任何一个状态下,每个原子只有两种状态,要么就是已经被消灭,要么就是还余下来。我们用0表示某个原子还存在,用1表示某个原子被消灭了,这样我们就可以用0到2^n-1个数字来表示任何一种状态了。
对于每一个状态i,我们可以从中挑选两个还存在的原子j和k来进行碰撞,看看碰撞后的能量有没有比原来的多。。。这个很纠结的说明白,我们看看状态转移方程吧
dp[i | (1<<k)] = max(dp[i|(1<<k)],dp[i]+map[j][k]);i表示第种状态,k表示碰撞后消失的原子,j表示碰撞后还存在的原子。等式左边的dp[i|1<<k]表示碰撞过后的状态的能量,等式右边的dp[i|1<<k]表示原来的能量,因为经过碰撞,k会消失,所以要用i|1<<k来表示。右边的dp[i]+map[j][k]表示在状态i中,经过j和k碰撞,余下j,变成i|1<<k状态后的新的能量。
i经过0到2^n-1次循环,每次循环都循环挑出任意两个还存在的原子碰撞后,最后的能量都储存到dp数组中。最后对dp检索出最大值,就是结果了。
/******************************************************************************* # Author : Neo Fung # Email : [email protected] # Last modified: 2011-10-17 08:54 # Filename: ZOJ3471 Most Powerful.cpp # Description : ******************************************************************************/ // #include "stdafx.h" // #define DEBUG #include <fstream> #include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <memory.h> #include <limits.h> using namespace std; int map[15][15]; int dp[1<<11]; int main(void) { #ifdef DEBUG freopen("data.txt","r",stdin); #endif int n; while(scanf("%d",&n)&&n) { int maxint=1<<n; for(int i=0;i<n;++i) for(int j=0;j<n;++j) scanf("%d",&map[i][j]); memset(dp,0,sizeof(dp)); for(int i=0;i<maxint;++i) //状态从0到2^n-1种 for(int j=0;j<n;++j) { if(i & (1<<j)) continue; //如果在状态i中第j个原子已消失,不计算 for(int k=0;k<n;++k) { if(i & (1<<k)) continue; //如果在状态i中第k个原子已消失,不计算 if(j==k) continue; //如果碰撞的两个原子相同,不计算 dp[i | (1<<k)] = max(dp[i|(1<<k)],dp[i]+map[j][k]); //状态转移方程 } } int ans=0; for(int i=0;i<maxint;++i) if(dp[i]>ans) ans=dp[i]; printf("%d\n",ans); } return 0; }