北大ACM2686——Traveling by Stagecoach~~状态压缩DP

本文探讨了状态压缩动态规划(DP)的概念及其在解决城市旅行问题中的应用,通过集合上的DP方法优化路径选择,考虑车票限制,实现最短时间计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近才看书,看到状态压缩。对于状态压缩DP,其实就是集合上的DP。

这需要我们了解一些位运算:

集合{0,1,2,3,....,n-1}的子集可以用下面的方法编码成整数

像这样,一些集合运算就可以用如下的方法来操作:

1.空集....................0

2.只含有第i个元素的集合{i}................1 << i

3.含有全部n个元素的集合{0,1,2,3,....,n - 1}.............(1 << n) - 1

4.判断第i个元素是否属于集合S.................................if(S >> i & 1)

5.向集合中加入第i个元素S ∪ {i}...............................S | 1 << i

6.从集合中除去第i个元素S \ {i}..................................S & ~(1 << i)

7.集合S和T的并集S∪T...............................................S | T

8.集合S和T的交集S∩T................................................S & T


而题目的意思是:一个人在m个城市的国家旅行,他有n张车票,这个国家有p条路,一条路连接两个城市,他要从a城市到b城市,从一个城市到另一个城市所需要的时间是路的长度除以车票的面值,面值是多少表示可以有多少匹马来拉,求最短的时间。


这题,不能直接用最短路径的算法来求解,有车票的限制。我们将在第i个城市看作一个状态,剩下的车票为一个集合S,从这个城市出发,使用一张车票到达城市j,现在他在第j个城市,状态为S \ {i},花费的时间就是路的长度除以车票的面值。

下面的是 AC的代码:

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

const double INF = 10000000000.00;
int n, m, p, a, b;
int ticket[10];
int map[32][32];

double dp[1 << 10][32];

double min(double a, double b)   //取最小值
{
	return a > b ? b : a;
}

void solve()
{
	int i, j;
	for(i = 0; i < 1 << n; i++) //初始化dp数组
	{
		for(j = 0; j < 32; j++)
			dp[i][j] = INF;
	}
	dp[(1 << n) - 1][a - 1] = 0;
	double res = INF;
	for(int S = (1 << n) - 1; S >= 0; S--)  //集合S
	{
		res = min(res, dp[S][b - 1]);
		for(int v = 0; v < m; v++)
		{
			for(i = 0; i < n; i++)        //枚举车票
			{
				if(S >> i & 1)            //判断第i个车票是否属于集合S
				{
					for(int u = 0; u < m; u++)
					{
						if(map[v][u] >= 0) //城市v到u有路,使用第i个车票从v到u
						{
							dp[S & ~(1 << i)][u] = min(dp[S & ~(1 << i)][u], dp[S][v] + double(map[v][u]) / ticket[i]);
						}
					}
				}
			}
		}
	}
	if(res == INF)
		printf("Impossible\n");
	else
		printf("%.3lf\n", res);
}

int main()
{
//	freopen("data.txt", "r", stdin);
	int i, x, y, z;
	while(cin >> n >> m >> p >> a >> b)
	{
		memset(map, -1, sizeof(map));
		if(n == 0 && m == 0 && p == 0 && a == 0 && b == 0)
			break;
		for(i = 0; i < n; i++)   //输入数据
			cin >> ticket[i];
		for(i = 0; i < p; i++)
		{
			cin >> x >> y >> z;
			map[x - 1][y - 1] = z;
			map[y - 1][x - 1] = z;
		}
		solve();	
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值