• 【Islands and Bridges】题解


    题目

    题目描述

    给定一些岛屿和一些连接岛屿的桥梁,大家都知道汉密尔顿路是访问每个岛屿一次的路线,在我们这个地图中,每个岛屿有个正整数的权值,表示这个岛屿的观赏价值。假设一共有N个岛屿,用Vi表示岛屿Ci的价值,汉密尔顿路C1C2....Cn的价值是以下三部分的总和:
      (1)所有岛屿的价值之和;
      (2)对于路径中相邻的两个岛屿CiCi+1,把两个岛屿的价值之积加到总价值中;
      (3)路径中连续三个岛屿CiCi+1Ci+2,如果Ci与Ci+2有桥直接相连,则把这三个岛屿价值之积加到总价值中。
      要求计算汉密尔顿路最大的价值以及方案数。

    输入

    输入第一行是一个整数Q(Q<=20),表示测试数据的数量。每个测试数据第一行输入两个整数N和M,分别表示岛屿数和桥梁数,接下来一行包含N个正整数,第i个数表示Vi,每个数不超过100,最后M行,每行两个数X,Y,表示岛X和岛Y之间有一座桥直接相连,桥是双向的,岛屿编号为1到N(N<=13)

    输出

    对于每个测试数据,输出一行,两个整数,第一个数表示最大价值,第二个数表示方案数,如果不存在汉密尔顿路径,输出“0 0”
    注意:一条路径可以反着走,我们认为这两条路径是同一条路径。

    样例输入

    2
    3 3
    2 2 2
    1 2
    2 3
    3 1
    4 6
    1 2 3 4
    1 2
    1 3
    1 4
    2 3
    2 4
    3 4
    

    样例输出

    22 3
    69 1
    

    分析

    对于第一问,因为N<=13,所以我们可以用状态压缩dp,压缩每个点是否走过的状态;当路径中连续三个岛屿CiCi+1Ci+2,Ci与Ci+2有桥直接相连时,就需要知道Ci和Ci+1这两个点。所以dp方程为f[i][j][k],指在状态i的时候,走到了k点,上一个走到的点是j的最大值。dp方程转移为:

    f[i+bb[l]][k][l]=max(f[i+bb[l]][k][l],f[i][j][k]+v[k]v[l]+v[l]) **//bb数组存2^i
    **f[i+bb[l]][k][l]=max(f[i+bb[l]][k][l],f[i][j][k]+v[k]
    v[l]+v[j]v[k]v[l]+v[l])
    //j和l有路径相通

    对于第二问,因为要求最大的价值的方案数,所以用g[i][j][k]指在状态i的时候,走到了k点,上一个走到的点是j的最大值的方案数。如果可以转移时但不相等就覆盖答案,只有在可以转移时并且相等才能累加答案。最后统计当价值为最大时的方案数。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    using namespace std;
    long long g[10001][20][20],f[10001][20][20];
    long long n,m,e,v[30];
    long long bb[30];
    bool b[30][30];
    int main()
    {
    	cin>>e;
    	long long x,y;
    	bb[1]=1;
    	int i,j,k,l;
    	for(i=2;i<=29;i++)
    	  	bb[i]=bb[i-1]*2;
    	while(e--)
    	{
    		scanf("%lld%lld",&n,&m);
    		for(i=0;i<=10000;i++)
    		    for(j=0;j<=19;j++)
    		        for(k=0;k<=19;k++)
    		        {
    		    		f[i][j][k]=0;
    		    		g[i][j][k]=0;
    				}
    		for(i=0;i<=n;i++)
    		    for(j=0;j<=n;j++)
    		    {
    		        b[i][j]=false;
    			}
    		for(i=1;i<=n;i++)
    		{
    			scanf("%lld",&v[i]);
    		}
    		for(i=1;i<=m;i++)
    		{
    			scanf("%lld%lld",&x,&y);
    			b[x][y]=b[y][x]=true;
    			f[bb[x]+bb[y]][x][y]=f[bb[x]+bb[y]][y][x]=v[x]*v[y]+v[x]+v[y];//初始化f数组
    			g[bb[x]+bb[y]][x][y]=g[bb[x]+bb[y]][y][x]=1;//初始化g数组
    		}
    		for(i=0;i<=bb[n+1]-1;i++)
    		{
    			for(j=1;j<=n;j++)
    				if((bb[j] | i)==i)        
    					for(k=1;k<=n;k++)
    			  			if((bb[k] | i)==i && b[j][k]){if(!g[i][j][k]) continue;
    			  				for(l=1;l<=n;l++)
    			  			    	if((bb[l] | i)!=i && b[k][l] && b[j][l])//当j和l有路径相同
    			  			    	{
    			  			        	if(f[i+bb[l]][k][l]==f[i][j][k]+v[k]*v[l]+v[j]*v[k]*v[l]+v[l])
    			  			        		g[i+bb[l]][k][l]+=g[i][j][k];
    									else
    									//当j和l无路径相同
    									if(f[i+bb[l]][k][l]<f[i][j][k]+v[k]*v[l]+v[j]*v[k]*v[l]+v[l])
    									{
    										f[i+bb[l]][k][l]=f[i][j][k]+v[k]*v[l]+v[j]*v[k]*v[l]+v[l];
    			  			        		g[i+bb[l]][k][l]=g[i][j][k];
    									}
    								}
    								else
    			  			    	if((bb[l] | i)!=i && b[k][l])
    			  			    	{
    			  			        	if(f[i+bb[l]][k][l]==f[i][j][k]+v[k]*v[l]+v[l])
    			  			        		g[i+bb[l]][k][l]+=g[i][j][k];
    									else
    									if(f[i+bb[l]][k][l]<f[i][j][k]+v[k]*v[l]+v[l])
    									{
    			  			            	f[i+bb[l]][k][l]=f[i][j][k]+v[k]*v[l]+v[l];
    			  			        		g[i+bb[l]][k][l]=g[i][j][k];
    									}
    								}}
    		}
    		long long ans=0;
    		long long ma=0;
    		for(i=1;i<=n;i++)
    		    for(j=1;j<=n;j++)
    		    {
    		    	if(f[bb[n+1]-1][i][j]>ma)
    		    	{
    		    		ma=f[bb[n+1]-1][i][j];
    		    		ans=g[bb[n+1]-1][i][j];
    				}
    				else
    				if(f[bb[n+1]-1][i][j]==ma)
    				{
    					ans+=g[bb[n+1]-1][i][j];
    				}
    			}
    		if(n==1) cout<<v[1]<<' '<<1<<endl;else
    		if(ma==0 || ans==0) cout<<0<<' '<<0<<endl;else
    		printf("%lld %lld
    ",ma,ans/2);
    	}
    }
    
    
    
  • 相关阅读:
    Java中如何利用File类递归的遍历指定目录中的所有文件和文件夹
    SQL的别名和SQL的执行顺序和SQL优化
    Linux中如何配置sudo用户
    Linux的ssh的known_host文件
    Linux的SSH服务
    Linux的图形模式和文本模式以及单用户模式切换
    Linux服务器磁盘空间占满问题
    PLSQL Developer连接本地Oracle 11g数据库
    Linux常用命令学习
    OAuth2 .net MVC实现获取token
  • 原文地址:https://www.cnblogs.com/chen1352/p/8992466.html
Copyright © 2020-2023  润新知