• poj 2288 Islands and Bridges_状态压缩dp_哈密尔顿回路问题


    题目链接

    题目描写叙述:哈密尔顿路问题。n个点,每个点有权值,设哈密尔顿路为 C1C2...Cn,Ci的权值为Vi,一条哈密尔顿路的值分为三部分计算:
    1.每个点的权值之和
    2.对于图中的每一条CiCi+1,加上Vi*Vi+1
    3.对于路径中的连续三个点:CiCi+1Ci+2,若在图中,三点构成三角形,则要加上Vi*Vi+1*Vi+2
    求一条汉密尔顿路能够获得的最大值,而且还要输出有多少条这种哈密尔顿路。

    这道题的状态感觉不是非常难想,由于依据一般的哈密尔顿路问题,首先想到的是设计二维状态,dp[i , s]表示当前在i点,走过的点形成状态集合s。可是这道题在求解值的时候有一个不一样的地方,就是第三部分,假设还是设计成二维的状态,就会非常麻烦,由于每添加一个新点,要推断新点、当前点、倒数第二个点是否构成三角形,所以要记录倒数第二个点。非常自然地想到扩展状态的维数,添加一维,记录倒数第二个点。

    1>  设计状态:
    dp[i , j , s]表示当前站在j点,前一个点是i点,形成的状态集合是s,此时的最大值,way[i , j , s]记录当前状态下达到最大值的路径数;
    2>  状态转移:
    设k点不在集合s中,且存在边<j , k>
    设q为下步到达k点获得的最大值
    令r = s + (1<<k),为当前站在点k,前一个点为j,形成状态集合r
    若i,j,k形成三角形,则q = dp[i][j][s] + v[k] + v[j]*v[k] + v[i]*v[j]*v[k]
    否则,q = dp[i][j][s] + v[k] + v[j]*v[k];
    若q大于dp[j][k][r];则:
    dp[j][k][r] = q
    way[j][k][r] = way[i][j][s];
    若q等于dp[j][k][r],则:
    way[j][k][r] += way[i][j][s];
    3>  初始化:
    显然,若i点到j点有边,则: 
    dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
    way[i][j][(1<<i)+(1<<j)] = 1;
    4>  结果的产生:
    最后的结果我们要枚举点i和j,找到最大的dp[i][j][(1<<n)-1],而且更新记录路径数ansp,最后ansp要除2才是结果,由于题目最后一句话,正向反向是一样的路。
    此外,须要注意的是discuss提到的特殊情况,要用__int64,而且注意n等于1时,最大值就是第一个点的权值,路径数为1。


    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int MAXN =13;
    const int MAXS =1<<MAXN|1;
    #define ll __int64 
    ll dp[MAXN][MAXN][MAXS],way[MAXN][MAXN][MAXS];
    int map[MAXN][MAXN],v[MAXN];
    int n,m,s;
    void stateDp(){
    	int i,j,p,k;
    	memset(dp,-1,sizeof(dp));
    	memset(way,0,sizeof(way));
    	for(i=0;i<n;i++)
    		for(j=0;j<n;j++){
    			if(map[i][j]){
    				dp[i][j][(1<<i)+(1<<j)]=v[i]+v[j]+v[i]*v[j];
    				way[i][j][(1<<i)+(1<<j)]=1;
    			}
    		}
    	for(p=3;p<s;p++){
    		for(i=0;i<n;i++){
    			if(!(p&1<<i))//假设该状态第i城市没有路过就跳过 
    				continue;
    			for(j=0;j<n;j++){
    				if(i==j||!(p&1<<j)||dp[i][j][p]==-1)
    					continue;
    				for(k=0;k<n;k++){
    					if(p&1<<k||!map[j][k])//假设k存在该状态则跳过 
    						continue;
    					int r=p+(1<<k);//状态增加k城市
    					ll q=dp[i][j][p]+v[k]+v[j]*v[k];//更新价值 
    					if(map[i][k]){//当构成环时更新价值 
    						q+=v[i]*v[j]*v[k];
    					} 
    					if(q>dp[j][k][r]){
    						dp[j][k][r]=q;
    						way[j][k][r]=way[i][j][p];
    					}else if(q==dp[j][k][r]){//相等时,有多个相等价值路径 
    						way[j][k][r]+=way[i][j][p];
    					}
    				}
    			} 
    		}
    	}
    }
    int main(int argc, char** argv) {
    	
    	int t,x,y,i,j;
    	scanf("%d",&t);
    	while(t--){
    		memset(map,0,sizeof(map));
    		scanf("%d%d",&n,&m);
    		for(i=0;i<n;i++)
    			scanf("%d",&v[i]);
    		for(i=0;i<m;i++){
    			scanf("%d%d",&x,&y);
    			map[x-1][y-1]=map[y-1][x-1]=1;
    		}
    		s=1<<n;
    		if(n==1){
    			printf("%d %d
    ",v[0],1);
    			continue;
    		}
    		stateDp();
    		ll ansv=-1,ansp=0;
    		for(i=0;i<n;i++)
    			for(j=0;j<n;j++){
    				if(i==j)continue;
    				if(dp[i][j][s-1]>ansv){//s-1为经过全部岛 
    					ansv=dp[i][j][s-1];
    					ansp=way[i][j][s-1];
    				}else if(dp[i][j][s-1]==ansv){
    					ansp+=way[i][j][s-1];
    				}
    			}
    		printf("%I64d %I64d
    ",ansv==-1?0:ansv,ansp/2);
    	}
    	return 0;
    }



  • 相关阅读:
    dom元素和方法总结
    jQuery插件开发
    单次遍历,带权随机选取问题
    转:面试中常见的一些算法问题
    树状数组资料
    逆序数的求法
    将n进制的数组压缩成字符串(0-9 a-z)同一时候解压
    [积累]C++复习 海大2014硕士生面试题微信系统总结
    记一个手游app数据文件的破解
    poj1189 简单dp
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/3746335.html
Copyright © 2020-2023  润新知