状态压缩DP——poj2288 Islands and Bridges 又见旅行商问题

题目链接:
http://poj.org/problem?id=2288
http://acm.hdu.edu.cn/showproblem.php?pid=1668


题目大意:
有n个岛屿,m座桥连接这些岛屿。遍历所有的岛屿且每个岛屿只能被遍历一次,这样就得到了一条哈密顿路。(n<=13)
根据公式,每条哈密顿路都可以计算出一个权值,求取最大的权值并且求出最大权值的哈密顿路的条数。
公式:
假设哈密顿路为 c1 c2 . .cn,总权值包括三部分
第1部分,v[i]之和
第2部分,v[i]*v[i+1]之和
第3部分,如果岛屿c[i]与岛屿c[i+2]也是相通的,即岛屿c[i],c[i+1],c[i+2]构成了一个三角关系,v[i]*[i+1]*[i+2]之和


解题思路:
岛屿的个数最多是13个,所以可以使用状态压缩来做,是典型的旅行商问题。
注意事项:
1、n=1的时候,路径的条数是1
2、如果哈密顿路不存在,输出“0 0”
3、哈密顿路的计数应该是用__int64,因为有13个岛屿,最多的路径数目为2的13次方,超过了int型的范围。
4、最后输出路径数目的时候应该除以2,因为题目说颠倒的路径只能算1条。


源代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;    //n表示岛屿的数目,m表示桥的数目,岛屿的数目不超过13
int Map[13][13];
int dp[(1<<14)][13][13];     //表示在状态i下,上一个岛屿是j,当前岛屿是k的路径值
__int64 cnt[(1<<14)][13][13];   //表示状态i下,上一个岛屿是j,当前岛屿是k的路径数目
int time[(1<<14)][13];       //表示在状态i下,岛屿j被访问的次数
int v[13];                  //表示每个岛屿的权值
int s[13];                  //状态总个数
void init()
{
    int i,t,j;
    s[0]=1;
    for(i=1;i<=n;i++)
    {
        s[i]=s[i-1]*2;  //2进制求取状态
    }
    for(i=0;i<s[n];i++)
    {
        t=i;
        for(j=0;j<n;j++)
        {
            time[i][j]=t%2;
            t=t/2;
        }
    }
    return;
}
void DP()
{
    int i,j,k,l,t,temp,flag;
    for(i=0;i<s[n];i++) //枚举所有状态
    {
        for(j=0;j<n;j++)    //枚举上一个点
        {
            if(time[i][j]==0)   continue;   //状态i不包含j点
            for(k=0;k<n;k++)    //枚举当前点
            {
                if(Map[j][k]==0)    continue;   //岛屿不可达
                if(time[i][k]==0 || j==k)   continue;
                if(dp[i][j][k]==-1) continue;   //表示状态是不可达的
                for(t=0;t<n;t++)    //枚举下一个点
                {
                    if(Map[k][t]==0)  continue;   //岛屿之间是不可达的
                    if(time[i][t]==1)   continue;   //岛屿t已经被访问过了
                    l=i+s[t];
                    temp=dp[i][j][k]+v[t]+v[k]*v[t];
                    if(Map[j][t])   //这个地方,我读错题了,没看到那个三角关系
                        temp+=v[j]*v[k]*v[t];
                    if(temp>dp[l][k][t])
                    {
                        dp[l][k][t]=temp;
                        cnt[l][k][t]=cnt[i][j][k];
                    }
                    else if(temp==dp[l][k][t])
                    {
                        cnt[l][k][t]+=cnt[i][j][k];
                    }
                }
            }
        }
    }
    int Max1=0;
    __int64 Max2=0;
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            if(dp[s[n]-1][i][j]>Max1 && dp[s[n]-1][i][j]!=INF)
            {
                Max1=dp[s[n]-1][i][j];
                Max2=cnt[s[n]-1][i][j];
            }
            else if(dp[s[n]-1][i][j]==Max1)
            {
                Max2+=cnt[s[n]-1][i][j];
            }
        }
    }
    if(Max1!=0) //注意:颠倒的路只能算一次,所以最后的路径数目需要除以2
        printf("%d %I64d\n",Max1,Max2/2);
    else    //没有回路的情况
        printf("0 0\n");
    return;
}
int main()
{
	//freopen("in.txt","r",stdin);
	int cs,i,j,k,t;
	scanf("%d",&cs);
	while(cs--)
	{
	    scanf("%d%d",&n,&m);
	    for(i=0;i<n;i++)
	    {
	        scanf("%d",&v[i]);
	    }
	    memset(Map,0,sizeof(Map));
	    while(m--)
	    {
	        scanf("%d%d",&i,&j);
	        Map[i-1][j-1]=Map[j-1][i-1]=1;
	    }
	    memset(dp,-1,sizeof(dp));
        init();
        if(n==1)    //一个点的情况
        {
            printf("%d 1\n",v[0]);
            continue;
        }
	    for(i=0;i<n;i++)    //枚举起点
	    {
	        for(j=0;j<n;j++)    //枚举第2个点
	        {
	            if(i==j)    continue;
	            if(Map[i][j]==0)    continue;
                dp[s[i]+s[j]][i][j]=v[i]+v[j]+v[i]*v[j];
                cnt[s[i]+s[j]][i][j]=1;
	        }
	    }
	    DP();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值