这题一开始没有什么想法,后来只能上网看别人的解题报告,看到了状态压缩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 : neosfung@gmail.com
# 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;
}