这种题我一直喜欢用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;
}