POJ

来源:互联网 发布:c语言指针教学视频 编辑:程序博客网 时间:2024/06/07 20:28

Islands and Bridges

Description

Given a map of islands and bridges that connect these islands, a Hamilton path, as we all know, is a path along the bridges such that it visits each island exactly once. On our map, there is also a positive integer value associated with each island. We call a Hamilton path the best triangular Hamilton path if it maximizes the value described below. 

Suppose there are n islands. The value of a Hamilton path C1C2...Cn is calculated as the sum of three parts. Let Vi be the value for the island Ci. As the first part, we sum over all the Vi values for each island in the path. For the second part, for each edge CiCi+1 in the path, we add the product Vi*Vi+1. And for the third part, whenever three consecutive islands CiCi+1Ci+2 in the path forms a triangle in the map, i.e. there is a bridge between Ci and Ci+2, we add the product Vi*Vi+1*Vi+2

Most likely but not necessarily, the best triangular Hamilton path you are going to find contains many triangles. It is quite possible that there might be more than one best triangular Hamilton paths; your second task is to find the number of such paths. 

Input

The input file starts with a number q (q<=20) on the first line, which is the number of test cases. Each test case starts with a line with two integers n and m, which are the number of islands and the number of bridges in the map, respectively. The next line contains n positive integers, the i-th number being the Vi value of island i. Each value is no more than 100. The following m lines are in the form x y, which indicates there is a (two way) bridge between island x and island y. Islands are numbered from 1 to n. You may assume there will be no more than 13 islands. 

Output

For each test case, output a line with two numbers, separated by a space. The first number is the maximum value of a best triangular Hamilton path; the second number should be the number of different best triangular Hamilton paths. If the test case does not contain a Hamilton path, the output must be `0 0'. 

Note: A path may be written down in the reversed order. We still think it is the same path.

Sample Input

23 32 2 21 22 33 14 61 2 3 41 21 31 42 32 43 4

Sample Output

22 369 1

Source

Shanghai 2004




题意:给你一个图,要你求一个哈密顿回路,使得这个哈密顿回路的三角哈密顿值最大,并计算这种回路有多少种。其中三角哈密顿值的计算方式由三部分组成

1.所有节点的和

2.相邻节点的乘积(头尾不算)

3.三个相邻节点,且这三个节点能形成三角形,的乘积(如果节点数大于三个)



解题思路:状态压缩DP,刚学状态压缩DP,要我自己想肯定想不出……感谢各位大佬的博客,想了很久终于搞懂了。重点就是怎么用dp数组表示状态的转移,很巧妙。

我们用dp[state][i][j]表示在状态state下,哈密顿路的倒数第二点为i,倒数第一的点为j的最大值,其中state为节点状态,即是否使用过。如110100代表3,5,6号节点在该路径内。

然后我们先初始化路径只有两个节点的时候的值(见代码),然后我们就可以从第三个节点开始枚举,开始状态转移。最后我们只要枚举i,j计算所有状态为11111····的值最大值即可。详见代码吧,注释满满。



#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <set>#include <vector>#include <map>#include <stack>#include <set>#include <algorithm>#include<queue>#define INF (1<<29)using namespace std;typedef long long ll;int N,M;int V[13];int G[13][13];int dp[1<<13][13][13];//开大会爆内存,所以开刚好13就好了ll num[1<<13][13][13];int main(){    int t;    scanf("%d",&t);    while(t--){        scanf("%d%d",&N,&M);        for(int i=0;i<N;i++)            scanf("%d",&V[i]);        memset(dp,-1,sizeof(dp));        memset(G,0,sizeof(G));        memset(num,0,sizeof(num));        int a,b;        for(int i=0;i<M;i++){            scanf("%d%d",&a,&b);            a--,b--;            G[a][b]=G[b][a]=1;        }        if(N==1){            printf("%d 1\n",V[0]);            continue;        }        //赋初始值,把所有只经过两个点的附初始值,后面的所有状态都从这个初始状态转移得来        for(int i=0;i<N;i++)            for(int j=0;j<N;j++)                if(i!=j&&G[i][j]){                    dp[(1<<i)|(1<<j)][i][j]=V[i]+V[j]+V[i]*V[j];                    num[(1<<i)|(1<<j)][i][j]=1;                }                //一定要想象0,1,10,11,100,101,110,111,1000这样子才能读懂下面代码……                for(int state=0;state<(1<<N);state++)//遍历所有状态,必须从小往大遍历才能实现状态转移,要想象那个二进制的1慢慢增多的样子        {            for(int i=0;i<N;i++)//枚举倒数第二个点            {                if((state&(1<<i))!=0)//如果在当前状态内                {                    for(int j=0;j<N;j++)//枚举倒数第一个点                    {                        if(G[i][j]&&i!=j&&(state&(1<<j))!=0&&dp[state][i][j]!=-1)//如果i,j连通,j在状态内,并且当前状态已经被计算过,之所以要计算过的,是因为我们要枚举第三个顶点。计算过的,才是成立的状态,即哈密顿回路可能存在                        {                            for(int k=0;k<N;k++)//枚举下一个点(就是准备成为最后一个点的点)                            {                                if(G[j][k]&&i!=k&&j!=k&&(state&(1<<k))==0)//如果j,k连通,并且该点不在状态内(因为是新加的点)                                {                                    int temp=dp[state][i][j]+V[k]+V[j]*V[k];//加上单个和和两个积的值                                    if(G[i][k])//如果i,k也连通,那么可以把三个积也算上                                        temp+=V[i]*V[j]*V[k];                                    //更新值,这个时候j,k就变为倒数第二和倒数第一,状态要加上k这个点(state|(1<<k))                                    if(dp[state|(1<<k)][j][k]<temp){                                        dp[state|(1<<k)][j][k]=temp;                                        num[state|(1<<k)][j][k]=num[state][i][j];                                    }                                    else{                                        if(dp[state|(1<<k)][j][k]==temp)//相同的话要叠加                                            num[state|(1<<k)][j][k]+=num[state][i][j];                                    }                                }                            }                        }                    }                }            }        }        //计算答案,哈密顿路,即状态为1111111···        int ans1=0;        ll ans2=0;        for(int i=0;i<N;i++)            for(int j=0;j<N;j++)                if(i!=j&&G[i][j]){                    if(ans1<dp[(1<<N)-1][i][j]){                        ans1=dp[(1<<N)-1][i][j];                        ans2=num[(1<<N)-1][i][j];                    }                    else{                        if(ans1==dp[(1<<N)-1][i][j])                            ans2+=num[(1<<N)-1][i][j];                    }                }        //一头一尾,最后要除2        printf("%d %lld\n",ans1,ans2/2);    }    return 0;}







原创粉丝点击