题目链接:
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条。
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;
}