poj2288

题目大意:求一条哈密顿回路,但是权值计算不同,包括三部分:1.经过的所有点的权值相加。2.经过的连续两个点的权值的乘积。3.能够构成三角型的连续三个点的乘积。这些全部加起来就是这条回路的总权值。输出最大权值和这个最大权值的路线有多少条


解题思路:先处理好两相连的情况,如果i与j,我们很容易得到dp[i][j][s]=(v[i]+v[j]+v[i]*v[j]);这相当于dp的初始化,i与j表                   示相连的两点,s表示当前走过的状态,接下来就是处理三点之间的关系了,当i与j满足相连关系时,枚举第                   三个点的所有情况,当他满足与j相连是,增加量ans=v[k]*v[j]+v[k], 若果k还满足了与i相连,那么         ans=ans+v[i]*v[j]*v[k];由于此题还要记录这样的路线数量,因此我们还需要一的三维数组记录到达各个路线的条数

当满足相等的情况是,num数组就应该相加进行更新,当新的路线权值大于旧的路线是,我们只需覆盖跟新,(想想为什么)(注意转移时的各种满足条件,不要漏写!!!)


代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL __int64
LL dp[15][15][1<<13],num[15][15][1<<13];
int map[13][13],a1[15],v[15];
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        int i,j,k,s,a,b;
        memset(map,0,sizeof(map));
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
        }
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            a--,b--;
            map[a][b]=map[b][a]=1;
        }
        a1[0]=1;
        for(i=1;i<=13;i++)
        {
            a1[i]=a1[i-1]*2;
        }
        if(n==1)//只有一个点时,直接输出第一个点的点权
        {
            printf("%d 1\n",v[0]);
        }
        else
        {
            for(i=0;i<n;i++)
            {
                for(j=0;j<n;j++)
                {
                    if(map[i][j]==0||i==j)continue;
                    int s=(a1[i]|a1[j]);
                    dp[i][j][s]=(v[i]+v[j]+v[i]*v[j]);
                    num[i][j][s]=1;//记录满足条件的路径有几条
                }
            }
            for(s=0;s<a1[n];s++)
            {
                for(i=0;i<n;i++)
                {
                    if((s&a1[i])==0)continue;
                    for(j=0;j<n;j++)
                    {
                        if(i==j||(s&a1[j])==0||dp[i][j][s]==0)continue;
                        if(map[i][j]==0)continue;
                        for(k=0;k<n;k++)
                        {
                            if(i==k||j==k||(s&a1[k])!=0||map[k][i]==0)continue;
                            int t=s|a1[k];
                            LL ans1=v[i]*v[k]+v[k];//k与i相连
                            if(map[k][j]!=0)//k与j相连满足,那么i,j,k三点两两相连
                            {
                                ans1+=v[i]*v[j]*v[k];
                            }
                            if(dp[k][i][t]<dp[i][j][s]+ans1)
                            {
                                dp[k][i][t]=dp[i][j][s]+ans1;
                                num[k][i][t]=num[i][j][s];
                            }
                            else if(dp[k][i][t]==dp[i][j][s]+ans1)
                            {
                                dp[k][i][t]=dp[i][j][s]+ans1;
                                num[k][i][t]+=num[i][j][s];
                            }
                        }
                    }
                }
            }
            LL ans=0,tol=0;
            for(i=0;i<n;i++)
            {
                for(j=0;j<n;j++)
                {
                    if(ans<dp[i][j][a1[n]-1])
                    {
                        ans=dp[i][j][a1[n]-1];
                        tol=num[i][j][a1[n]-1];
                    }
                    else if(ans==dp[i][j][a1[n]-1])
                    {
                        ans=dp[i][j][a1[n]-1];
                        tol+=num[i][j][a1[n]-1];
                    }
                }
            }
            printf("%I64d %I64d\n",ans,tol/2);//除以2的原因是因为无向图,而i到j,j到i我们各算了一次
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值