题意就是给你一些点,有的地方有景区,有的地方有某些景点的优先票,拿优先票去景点和不拿的时间不一样。问从地点1出发去所有景点再回来的最短时间。
得把景点,景区进行状压。即dp[i][j][k]代表参观景点状态为i,景区状态为j,停在k的最短时间。
先floyd一下处理出最短路。
对于一种状态i,j,为了保证dp[i][j][k]在递推的时候是最短时间可以先用2个(1-n)for循环进行更新,因为可能有的位置的状态是-1即不存在。事实上只要有一个点存在i与j的状态,那么其他点必定都存在i,j这个状态。
然后再去更新去看景点的情况,在做过刚才那个处理后,其实只需要看当前位置下的景点就行,看网上其他人的代码则是遍历去看所有景点,如果这样做的话就不用先做那个处理了,可以放在一起做。
另外,还有一个优化,如果已经参观了那个节点,那么就强行给他一个票,其实就是在其他条件都相同的情况下(在同一个点,经过同样的景点),有票跟没票的比较。这样的话,参观景点的集合就是票的集合的子集了。然后又容易看出其实i跟j的递推顺序交换也是一样的,所以就用了枚举子集的办法,然后把子集从小到大推。
竟然刷到rank1了。577ms。
AC代码:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned long long
#define eps 1e-8
#define NMAX 1<<29
#define MOD 1000000
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
char c;
int flag = 0;
ret=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c == '-')
{
flag = 1;
c = getchar();
}
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
if(flag) ret = -ret;
}
int dp[1<<8][1<<8][55],dist[55][55];
struct node
{
int t,ft;
};
node no[10];
inline void update(int &a, int b)
{
if(a < 0 || a > b)
a = b;
}
void floyd(int n)
{
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);
}
int pos[10],getft[55];
int s[1<<8];
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o.txt","w",stdout);
#endif
int t,n,m,K,cas = 1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&K);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
dist[i][j] = NMAX;
dist[i][i] = 0;
}
for(int i = 1; i <= m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dist[u][v] = dist[v][u] = w;
}
floyd(n);
memset(getft,0,sizeof(getft));
for(int i = 0; i < K; i++)
{
int tmp;
scanf("%d%d%d%d",&pos[i],&no[i].t,&no[i].ft,&tmp);
while(tmp--)
{
int t1;
scanf("%d",&t1);
getft[t1] |= (1<<i);
}
}
for(int i = 0; i < (1<<K); i++)
for(int j = 0; j < (1<<K); j++)
for(int k = 1; k <= n; k++) dp[i][j][k] = -1;
dp[0][0][1] = 0;
for(int j = 0; j < (1<<K); j++)
{
int nct = 0;
for(int i = j; i ; i = (i-1)&j) s[nct++] = i;
s[nct++] = 0;
for(int w = nct-1; w >= 0; w--)
{
int i = s[w];
for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)
{
for(int l = 0; l < K; l++) if(!(i&(1<<l)))
{
int cost = dist[k][pos[l]];
if(j&(1<<l)) cost += no[l].ft;
else cost += no[l].t;
update(dp[i|(1<<l)][j|getft[pos[l]]|(1<<l)][pos[l]],dp[i][j][k]+cost);
}
for(int l = 1; l <= n; l++)
update(dp[i][j|getft[l]][l],dp[i][j][k]+dist[k][l]);
}
// for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)//这种递推就可以只看自己点上面的景点
// {
// for(int l = 1; l <= n; l++)
// update(dp[i][j|getft[l]][l],dp[i][j][k]+dist[k][l]);
// }
// for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)
// {
// for(int l = 0; l < K; l++) if(!(i&(1<<l)))
// {
// if(pos[l] != k) continue;
// int cost;
// if(j&(1<<l)) cost = no[l].ft;
// else cost = no[l].t;
// update(dp[i|(1<<l)][j|getft[pos[l]]|(1<<l)][pos[l]],dp[i][j][k]+cost);
// }
// }
}
}
printf("Case #%d: %d\n",cas++,dp[(1<<K)-1][(1<<K)-1][1]);
}
return 0;
}