[POI2007]旅游景点atr dij+状压DP

本文介绍了一种使用状态压缩动态规划(状压DP)解决带有特定限制条件的最短路径问题的方法。在一个含有多个必经点的图中,通过预处理最短路关系并应用状压技巧,有效地解决了在某些点停留前必须先停留另一些点的问题。文章详细展示了算法实现,包括数据结构定义、Dijkstra算法的应用以及最终答案的求解过程。

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

Description
给你一张n个点,m条边的图,有K个必经点,分别是:2,3,…,K+1,经过一个点,可以选择停留,也可以不停留,这里的毕竟是指停留,给出q个限制条件,要求满足在某个点停留之前,某个点已经停留过。


Sample Input
8 15 4
1 2 3
1 3 4
1 4 4
1 6 2
1 7 3
2 3 6
2 4 2
2 5 2
3 4 3
3 6 3
3 8 6
4 5 2
4 8 6
5 7 4
5 8 6
3
2 3
3 4
3 5


Sample Output
19


首先你可以先预处理出K个点之间互相的最短路关系。
然后考虑状压,然后就是一个比较水的状压题了。。。


#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

struct node {
	int x, d;
	friend bool operator < (node a, node b) {return a.d > b.d;}
}; priority_queue<node> q;
struct edge {
	int x, y, c, next;
} e[410000]; int len, last[21000];
int K, s[22], id[22], D[22][22], d[21000];
int f[21][1048576];
bool v[21000];

void ins(int x, int y, int c) {
	e[++len].x = x, e[len].y = y; e[len].c = c;
	e[len].next = last[x], last[x] = len;
}

void dij(int st, int hh) {
	memset(v, 0, sizeof(v));
	memset(d, 63, sizeof(d)); d[st] = 0;
	node tmp; tmp.x = st, tmp.d = 0;
	q.push(tmp);
	while(!q.empty()) {
		node now = q.top(); q.pop();
		int x = now.x;
		if(v[x]) continue; v[x] = 1;
		for(int k = last[x]; k; k = e[k].next) {
			int y = e[k].y;
			if(d[y] > d[x] + e[k].c) {
				d[y] = d[x] + e[k].c;
				if(!v[y]) {tmp.x = y, tmp.d = d[y]; q.push(tmp);}
			}
		}
	} for(int i = 1; i <= K + 1; i++) if(id[i] != st) D[hh][i] = d[id[i]];
}

int main() {
	int n = read(), m = read(); K = read();
	for(int i = 1; i <= m; i++) {
		int x = read(), y = read(), c = read();
		ins(x, y, c), ins(y, x, c);
	} id[0] = 1;
	for(int i = 1; i <= K; i++) id[i] = i + 1;
	id[K + 1] = n; for(int i = 0; i <= K; i++) dij(id[i], i);
	if(K == 0) {printf("%d\n", D[0][1]); return 0;}
	int q = read();
	for(int i = 1; i <= q; i++) {
		int x = read() - 1, y = read() - 1;
		if(x > K || y > K) continue;
		s[y] |= 1 << x - 1;
	} memset(f, -1, sizeof(f));
	f[0][0] = 0;
	for(int i = 0; i < (1 << K); i++) {
		for(int j = 0; j <= K; j++) if(f[j][i] != -1){
			for(int k = 1; k <= K; k++) if((i & s[k]) == s[k] && !(i << k - 1 & 1)){
				if(f[k][i | (1 << k - 1)] == -1) f[k][i | (1 << k - 1)] = f[j][i] + D[j][k];
				else f[k][i | (1 << k - 1)] = _min(f[k][i | (1 << k - 1)], f[j][i] + D[j][k]);
			}
		}
	} int ans = 999999999;
	for(int i = 1; i <= K; i++) if(f[i][(1 << K) - 1] != -1){
		ans = _min(ans, f[i][(1 << K) - 1] + D[i][K + 1]);
	} printf("%d\n", ans);
	return 0;
}

### 关于缩动态规划(DP)的相关题目与教程 #### 洛谷平台上的资源推荐 洛谷作为一个优质的在线编程练习网站,提供了大量有关缩动态规划的学习资料和实战题目。对于想要深入理解并掌握这一复杂算法的人来说,这些资源非常有价值。 #### 推荐的经典入门题库 - **P1433 吃奶酪**:这道题不仅涉及到了深度优先搜索加缩的思想,还融合了动态规划的概念,在解决过程中能够很好地锻炼选手对多种算法组合运用的能力[^2]。 - **PRZ - POI2004**:此问题同样是一道典型的缩动态规划应用实例,通过该题目的训练可以帮助加深对这类问题处理方法的理解[^3]。 #### 学习路径建议 为了更好地理解和实践缩动态规划,建议按照如下顺序逐步推进: - 熟悉二进制位运算操作的基础知识,这是实现高效编码的关键技能之一; - 复习基本的动态规划理论及其常见应用场景; - 结合具体案例研究如何定义合适的态表示形式以及设计有效的态转移方程; ```python def dp_solution(): """ 这里提供了一个简化版的缩动态规划框架, 实际编写时需根据具体问题调整参数设置及逻辑流程。 """ n = ... # 输入规模大小 state_size = 1 << n # 总共可能存在的不同态数 # 初始化记忆化数组,默认值设为无穷大或其他不可能取到的最大/最小边界条件 memo = [-1] * state_size def dfs(current_state, current_index): nonlocal memo if all_bits_set(current_state): return cost_to_end[current_index] if memo[current_state] != -1: return memo[current_state] min_cost = float('inf') for next_index in range(n): new_state = update_state_with_next_move(current_state, next_index) transition_cost = calculate_transition_expense(current_index, next_index) total_cost = transition_cost + dfs(new_state, next_index) min_cost = min(min_cost, total_cost) memo[current_state] = min_cost return min_cost result = dfs(initial_state(), start_position()) return result ``` 上述代码片段展示了利用函数`dfs()`来进行带备忘录的记忆化搜索过程,其中包含了几个重要的组成部分如初始化全局变量、判断终止条件、剪枝优化等技巧来提高求解效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值