这种题我一直喜欢用bfs搞的,但是这个题不太好bfs,主要是我刚开始想的是通过边进行状态转移,这样很不好写。。。于是就坑爹了,调了很久sample都没出。。。
于是学习了一下别人的思想。。。通过“目的”来进行状态转移而不是边。当你在某个点的时候,你可以为了游历某个景点去某个点,也可以为了获取某个点的票而去某个点。能想到这一点的话状态转移方程就很好写了,但是也要能想到啊。。。
ps:注意代码中的(1) 跟(2)的关系。
#include<algorithm> #include<iostream> #include<cstring> #include<fstream> #include<sstream> #include<vector> #include<string> #include<cstdio> #include<bitset> #include<queue> #include<stack> #include<cmath> #include<map> #include<set> #define FF(i, a, b) for(int i=a; i<b; i++) #define FD(i, a, b) for(int i=a; i>=b; i--) #define REP(i, n) for(int i=0; i<n; i++) #define CLR(a, b) memset(a, b, sizeof(a)) #define debug puts("**debug**") #define LL long long #define PB push_back #define MP make_pair #define eps 1e-8 using namespace std; const int maxn = 55; const int INF = 1e9; int T, n, m, k, tot, u, v, w, ni; int dp[1<<8][1<<8][maxn], g[maxn][maxn], p[10], t[10], ft[10]; LL in[10]; //in[i]:第i个景点的票在那些点能拿到 inline int bit(int x) { return 1<<x; } inline void CheckMin(int &a, int b) { if(a == -1 || a > b) a = b; } //x点能拿到那些景点的票 inline int has(int x) { int ret = 0; REP(i, k) if(in[i]&(1LL<<x)) ret |= 1<<i; return ret; } void pre() { scanf("%d%d%d", &n, &m, &k); CLR(g, -1); CLR(in, 0); REP(i, m) { scanf("%d%d%d", &u, &v, &w); CheckMin(g[u][v], w); g[v][u] = g[u][v]; } REP(i, k) { scanf("%d%d%d%d", &p[i], &t[i], &ft[i], &ni); while(ni--) { scanf("%d", &u); in[i] |= 1LL<<u; } } } void floyd() { FF(k, 1, n+1) FF(i, 1, n+1) if(i != k && g[i][k] != -1) FF(j, 1, n+1) if(k != j && g[k][j] != -1) { if(i == j) g[i][j] = 0; else CheckMin(g[i][j], g[i][k] + g[k][j]); } } int solve() { //dp[vis][have][i]: //已经游历过vis状态的点,拥有have状态的票 目前在i城市的最小耗时 CLR(dp, -1); dp[0][0][1] = 0; tot = 1<<k; REP(vis, tot) REP(have, tot) if((vis&have) == vis) //vis是have的真子集......(1) FF(i, 1, n+1) if(dp[vis][have][i] != -1) { REP(j, k) if((vis & bit(j)) == 0)//未游离j景点 { //去游历j景点 获得该点的所有票 //游历j景点不一定需要票 但游历后默认为有j票 ......(2) int sta = has(p[j]); CheckMin(dp[vis|bit(j)][have|sta|bit(j)][p[j]], dp[vis][have][i] + g[i][p[j]] + (((have|sta)&bit(j)) ? ft[j] : t[j])); } FF(j, 1, n+1) { //去某点获得该点的所有票 int sta = has(j); if(sta == have) continue; if((sta&have) != sta) CheckMin(dp[vis][have|sta][j], dp[vis][have][i] + g[i][j]); } } int ret = INF; REP(have, tot) FF(i, 1, n+1) if(dp[tot-1][have][i] != -1) CheckMin(ret, dp[tot-1][have][i] + g[i][1]); return ret; } int main() { scanf("%d", &T); FF(kase, 1, T+1) { pre(); floyd(); printf("Case #%d: %d\n", kase, solve()); } return 0; }