poj 2288

本文详细介绍了一种使用状态压缩动态规划(状压DP)解决路径条数问题的方法,通过记录最大值及其方案数,实现了O(2^n*n^2)的时间复杂度,特别注意使用longlong数据类型以避免溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

解题思路

状压dp,记录路径条数,dp[S][i][j]表示状态为S,前一个点是i,再前一个点是j的最大值,然后在开个一样的数组记录方案数,时间复杂度O(2^n*n^2),注意要用long long,还有数据有一个点的情况。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long

using namespace std;
const int N = 14;
typedef long long LL;
const int inf = -0x3f3f3f3f;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}

int q,n,m,a[N][N],val[N];
LL dp[1<<N][N][N],num[1<<N][N][N],ans,ans_num;

signed main(){
    q=rd();
    while(q--){
        ans=0,ans_num=0;
        memset(a,0,sizeof(a));
        memset(dp,-0x3f,sizeof(dp));
        memset(num,0,sizeof(num));
        n=rd(),m=rd();register int x,y;
        for(register int i=1;i<=n;i++) val[i]=rd();
        for(register int i=1;i<=m;i++) {
            x=rd(),y=rd();
            a[x][y]=a[y][x]=1;
        }
        dp[0][0][0]=0;num[0][0][0]=1;
        for(register int i=0;i<(1<<n)-1;i++)
            for(register int j=1;j<=n;j++)if(!((1<<j-1)&i))
                for(register int k=0;k<=n;k++)if(((1<<k-1)&i && a[j][k]) || k==0)
                    for(register int p=0;p<=n;p++)if(((1<<p-1)&i && a[p][k]) || p==0){
                        if(dp[i][k][p]==-inf) continue;
                        LL now=dp[i][k][p]+val[j]+(LL)val[j]*val[k];
                        if(a[j][p]) now+=(LL)val[j]*val[k]*val[p];
                        if(dp[i|(1<<j-1)][j][k]==now)
                            num[i|(1<<j-1)][j][k]+=num[i][k][p];
                        else if(dp[i|(1<<j-1)][j][k]<now){
                            dp[i|(1<<j-1)][j][k]=now;
                            num[i|(1<<j-1)][j][k]=num[i][k][p];
                        }
                    }
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=n;j++)  
                if(a[i][j]) {
                    if(dp[(1<<n)-1][i][j]>ans) {ans=dp[(1<<n)-1][i][j];ans_num=num[(1<<n)-1][i][j];}
                    else if(dp[(1<<n)-1][i][j]==ans) ans_num+=num[(1<<n)-1][i][j]; 
                }
        if(n==1 && m==0) {ans=val[1];ans_num=2;}
        cout<<ans<<" "<<ans_num/2<<endl;
    }
    return 0;
}

转载于:https://www.cnblogs.com/sdfzsyq/p/9676834.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值