ZOJ3471

ZOJ3471 Most Powerful

有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.

AC代码:30ms

#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int v[15][15];
int d[1<<10];
int main()
{
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&v[i][j]);
        memset(d,-1,sizeof(d));
        d[0]=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)if(i!=j)//用j撞i销毁i
                d[(1<<i)]=max( d[(1<<i)] , v[j][i] );
        int ans = 0;
        for(int S=0;S<(1<<n);S++)//枚举所有子状态
        {
            for(int i=0;i<n;i++)if( !(S&(1<<i)) && d[S]!=-1)//i不在S中
            {
                for(int j=0;j<n;j++)if(j!=i && !(S&(1<<j)))//j不在S中,从j到i或从i到j
                {
                    d[S|(1<<i)] = max(d[S|(1<<i)],d[S]+v[j][i]);//从j到i,废弃i
                    d[S|(1<<j)] = max(d[S|(1<<j)],d[S]+v[i][j]);//从i到j,废弃j
                    ans = max(ans,max(d[S|(1<<j)],d[S|(1<<i)]));
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值