HDU 1668 POJ 2288 Islands and Bridges

以下解题思路摘自:http://www.cnblogs.com/jackge/archive/2013/05/24/3096162.html

取dp[state][i][j]表示state状态下倒数第二个岛为i,最后一个岛为j时的最优解,num[state][i][j]为相应的路径数目,其中state的二进制表示的i位为1表示岛i被访问过,反之为0。
则显然当有边(i,j)存在时,有如下初值可赋:
dp[(1<<i)+(1<<j)][i][j]=val[i]+val[j]+val[i]*val[j],num[(1<<i)+(1<<j)][i][j]=1。
如果状态(state,i,j)可达,检查岛k,如果此时k没有被访问过并且有边(j,k)存在,则做如下操作:
1)设tmp为下一步访问岛k时获得的总利益,r=state+(1<<k)。
2)如果tmp>dp[r][j][k],表示此时可以更新到更优解,则更新:
    dp[r][j][k]=tmp,num[r][j][k]=num[state][i][j]。
3)如果tmp==dp[r][j][k],表示此时可以获得达到局部最优解的更多方式,则更新:
    num[r][j][k]+=num[state][i][j]。
最后检查所有的状态((1<<n)-1,i,j),叠加可以得到最优解的道路数。
需要注意的是,题目约定一条路径的两种行走方式算作一种,所以最终结果要除2。

以下代码实现为我自己写的:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define max_inf 0x7f7f7f7f
#define min_inf 0x80808080
#define mod 1000000007

int n , m , dp[1<<13][13][13] , val[15];
int64 sum[1<<13][13][13];
bool Map[15][15];


void Solve()
{
	int i , j , k , l , up = 1<<n , next;
	memset(dp , -1 , sizeof dp);
	memset(sum , 0 , sizeof sum);

	//给相邻点赋初值
	for (i = 0 ; i < n ; i++)
	{
		for (j = 0 ; j < n ; j++)
		{
			if (i != j && Map[i][j])
			{
				dp[(1<<i)+(1<<j)][i][j] = val[i]+val[j]+val[i]*val[j];
				sum[(1<<i)+(1<<j)][i][j] = 1;
			}
		}
	}
	for (i = 1 ; i < up ; i++)
	{
		for (j = 0 ; j < n ; j++)
		{
			if ( !(i & (1<<j)) )continue;
			for (k = 0 ; k < n ; k++)
			{
				
				if (j == k)continue;
				if ( !(i & (1<<k)) )continue;
				if (dp[i][j][k] < 0)continue;

				//找寻下一个可以连接的点
				for (l = 0 ; l < n ; l++)
				{
					if (l == j || l == k)continue;
					if (i & (1<<l))continue;
					if (!Map[k][l])continue;
					int tmp = dp[i][j][k]+val[l]+val[k]*val[l];
					if (Map[j][l])tmp += val[j]*val[k]*val[l];
					next = i+(1<<l);
					if (dp[next][k][l] == tmp)
						sum[next][k][l] += sum[i][j][k];
					else if (dp[next][k][l] < tmp)
					{
						dp[next][k][l] = tmp;
						sum[next][k][l] = sum[i][j][k];
					}
				}
			}
		}
	}
	int MAX = -1;
	int64 ans = 0;
	for (i = 0 ; i < n ; i++)
	{
		for (j = 0 ; j < n ; j++)
		{
			
			if (i == j)continue;
			if (MAX < dp[up-1][i][j])
			{
				MAX = dp[up-1][i][j];
				ans = sum[up-1][i][j];
			}
			else if (MAX == dp[up-1][i][j])
				ans += sum[up-1][i][j];
		}
	}
	if (MAX < 0)printf("0 0\n");
	else printf("%d %I64d\n",MAX , ans/2);
}

int main()
{
	int t , i;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		memset(Map , 0 , sizeof Map);
		for (i = 0 ; i < n ; i++)scanf("%d",val+i);
		for (i = 0 ; i < m ; i++)
		{
			int a , b;
			scanf("%d%d",&a,&b);
			a--,b--;
			Map[a][b] = Map[b][a] = 1;
		}
		if (n == 1)
			printf("%d 1\n",val[0]);
		else
			Solve();
	}
	return 0;
}


 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值