Islands and Bridges

题目:http://poj.org/problem?id=2288

这是一道状态压缩dp

看到这题知道是用动态规划,猜想是用三维数组的,,但是不知道是状态压缩dp,今天刚学这个状态dp,这种状态压缩dp挺神奇的,只是怎么表示状态真难搞,花了一下午的时间看了几个大佬的博客,基本懂了,我真是太菜了~~

进入正题

首先分析问题
1.每个岛屿有一个值
2.两个相连的边要添加两个岛屿值相乘得到的值
3. 构成三角形,第一个岛屿和最后一个岛屿的边值不要计算到结果里去
4. 哈密顿路径值 = 所有岛屿的值 + 加上构成三角形的岛屿的边值(注意第三条)+三个岛屿值的乘积
5. 找出最大的哈密顿路径值和方案数

int dp[1 << N][N][N]; //存储岛屿之间的哈密顿路径值 第一维表示状态
ll num[1 << N][N][N]; //存储方案数 第一维表示状态


	for(int i = 0;i<n;i++)  //这里是把两个岛屿相连的哈密顿路径值算出来,因为后面计算要用
		for (int j = 0; j < n; j++) {
			
			if (i != j && g[i][j]) {  //判断岛是否相连
				//这里的位运算就是关键,,,你得把他看成二进制根据1出现的位置来表示哪两个岛屿相连
				dp[(1 << i) | (1 << j)][i][j] = a[i] + a[j] + a[i] * a[j];  //把两个岛屿的值以及两个岛屿的值乘积加起来
				num[(1 << i) | (1 << j)][i][j] = 1;//方案数是1
			}
		}

上面这部分比较关键
在这里插入图片描述

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

typedef long long ll;

const int N = 13;	//最大岛屿数量

int dp[1 << N][N][N]; //存储岛屿之间的哈密顿路径值
ll num[1 << N][N][N]; //存储方案数
int g[N][N];

int main() {

	int q;
	cin >> q;

	while (q--) {

		//初始化
		memset(dp, -1, sizeof(dp));
		memset(num, 0, sizeof(num));
		memset(g, 0, sizeof(g));

		int n, m;
		cin >> n >> m; //岛屿数量,桥梁数量

		int a[N];

		for (int i = 0; i < n; i++)  //每个岛屿的值
			cin >> a[i];

		int u, v;
		for (int i = 0; i < m; i++) {  //各岛屿相连的边
			cin >> u >> v;
			u--, v--; //这里要-1,因为后面有位运算,要下标一致
			g[u][v] = g[v][u] = 1;
		}

		for(int i = 0;i<n;i++)  //这里是把两个岛屿相连的哈密顿路径值算出来,因为后面计算要用
			for (int j = 0; j < n; j++) {
				
				if (i != j && g[i][j]) {  //判断岛是否相连
					//这里的位运算就是关键,,,你得把他看成二进制根据1出现的位置来表示哪两个岛屿相连
					dp[(1 << i) | (1 << j)][i][j] = a[i] + a[j] + a[i] * a[j];  //把两个岛屿的值以及两个岛屿的值乘积加起来
					num[(1 << i) | (1 << j)][i][j] = 1;//方案数是1
				}
			}

		if (n == 1) {  //只有一个岛屿的时候,特判
			cout << a[0] <<" 1"<< endl;
			continue;
		}

		for (int state = 0; state < (1 << n); state++) {  //把所有状态遍历一次
			
			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) { //两个岛屿是否相连,是不是同一个岛屿,第二个岛屿在不在当前状态下,在当前状态下dp值是否计算过

							for (int k = 0; k < n; k++) {  //找第三个岛屿,在多个岛屿中,指的是倒数第0个岛屿

								if (i != k && j != k && (state & (1 << k)) == 0 && g[j][k]) {  //判断第三个与另外两个岛屿是否相等,通过与运算判断第三个岛屿在不在当前状态下,一定要不在

									int temp = dp[state][i][j] + a[k] + a[j] * a[k];  //把前面计算了两个岛屿的dp值+当前岛屿的值+第二个岛屿和第三个岛屿的值乘积全都加起来

									if (g[i][k])  //看第一个岛屿与第三个岛屿是否相连,如果相连就构成三角形
										temp += a[i] * a[j] * a[k]; //构成三角形那么就要再加上三个岛屿值的乘积

									if (dp[(state | (1 << k))][j][k] < temp) { //注意这里的下标,状态,通过或运算可以得到三个岛屿的状态,而后面的变成了第二个岛屿和第三个岛屿,再比较当前状态下这三个岛屿的dp值是否大于刚才计算的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];
									
								}
							}
						}
					}
				}
			}
		}

		int ans1 = 0; //哈密顿路径最大值
		ll ans2 = 0; //方案数

		for(int i = 0;i<n;i++)  
			for (int j = 0; j < n; j++) {
				if (dp[(1<<n)-1][i][j] > ans1) {
					ans1 = dp[(1 << n) - 1][i][j];  //这部分的位运算就是找到每一位都是1的状态,也就是所有岛屿都考虑了的状态
					ans2 = num[(1 << n) - 1][i][j];
				}else if(dp[(1 << n) - 1][i][j] == ans1)
					ans2 += num[(1 << n) - 1][i][j];
			}

		cout << ans1 << " " << (ans2 >> 1) << endl; //方案数要除以2,因为一头一尾都加了,就表示方案数家里两遍

	}

	
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值