大致题意
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;
}