POJ 3441 状态压缩DP

大致题意

m 条需要付费的道路连接 N 座城市,付费方式有 2 种,若城市 ai 至 bi 的路径中经过 ci 这座城市,则付费 Pi,否则付费 Ri。
1 ≤ m, N ≤ 10, 0 ≤ Pi , Ri ≤ 100, Pi ≤ Ri (1 ≤ i ≤ m)

状态为经过城市的集合,以判断当前路径的付费情况。

状态转移,dp[s | 1 << e.to][e.to] = min(dp[s | 1 << e.to][e.to], dp[s][e.from] + cost),e.to 为目的节点,e.from 为出发节点,cost 为经过这条边的花费。

由于 Pi ≤ Ri ,经过某个特定节点可能会让边的权值减小,所以前往该特定节点再返回也是最小花费的一种可能情况。所以状态转移前不能 if ( !(s >> e.to & 1) ) 判断是否经过该节点。

考虑遍历顺序, (s | 1 << e.to) >= s ,状态递增即可。关键在于边,当 (s | 1 << e.to) == s ,可能会出现环。考虑到经过节点的目的是前进或者加入集合(再折返),所以只要预先对边按照目的结点升序排序,每一个状态遍历一遍边即可求解。

#include <cstdio>
#include <STDLIB.H>
#include <cmath>
#include <algorithm>
#include <iostream>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define eps 1e-4
#define M_PI 3.14159265358979323846
#define MAX_M 10
#define MAX_N 10
using namespace std;
struct edge{
	int from, to, via;
	int p, r;
	edge(int a, int b, int c, int p, int r): from(a), to(b), via(c), p(p), r(r) {}
	edge(){}
};
int N, M;
edge es[MAX_M];
int dp[1 << MAX_N][MAX_N];

bool cmp(const edge& e1, const edge& e2){
	return e1.to < e2.to;
}
void init(){
	for(int i = 0; i < M; i++){
		int a, b, c, p, r;
		scanf("%d%d%d%d%d", &a, &b, &c, &p, &r);
		--a, --b, --c;
		es[i] = edge(a, b, c, p, r);
	}
	sort(es, es + M, cmp);
}
void solve(){
	memset(dp, 0x3f, sizeof(dp));
	dp[1][0] = 0;
	for(int s = 0; s < 1 << N; s++){
		for(int i = 0; i < M; i++){
			edge e = es[i];
			if(dp[s][e.from] != INF){
				int cost = (s >> e.via & 1) ? e.p : e.r;
				dp[s | 1 << e.to][e.to] = min(dp[s | 1 << e.to][e.to], dp[s][e.from] + cost);
			}
		}
	}
	int res = INF;
	for(int s = 0; s < 1 << N; s++) res = min(res, dp[s][N - 1]);
	if(res == INF) printf("impossible\n");
	else printf("%d\n", res);
}
int main(){
	while(~scanf("%d%d", &N, &M)){
		init();
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值