题目大意:大陆上有n个村庄,m条双向道路,p种怪物,k个铁匠,每个铁匠会居住在一个村庄里,你到了那个村庄后可以让他给你打造剑,每个铁匠打造的剑都可以对付一些特定种类的怪物,每条道路上都可能出现一些特定种类的怪物,每条道路都有一个通过所需要的时间,现在要从1走到n,初始的时候你没有剑,要求在经过一条道路的时候,对于任意一种可能出现在这条道路上的的怪物,你都有已经有至少一把剑可以对付他,求从1走到n的最短时间(打造剑不需要时间)
没有很多人做的原因是因为之前题面有问题?
p<=13,那一看就是要记状态了
然后又要求求最短路
第一下想到状压DP,发现没有拓扑序,所以就只好dijkstra了
d[i][j]记录到i号点,现在能打怪的状态是j,最少需要多长时间
转移的时候判断一下这条边能不能走就可以了
虽说200W个点还是有点虚的,不过实际好像跑的还挺快,256ms
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define mp(x,y,z) (make_pair(make_pair(x,y),z))
#define N 6010
using namespace std;
int a[210];
int to[N],nxt[N],pre[210],cnt,t[N],w[N];
void ae(int ff,int tt,int TT,int ww)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
pre[ff]=cnt;
t[cnt]=TT;
w[cnt]=ww;
}
int d[210][1<<13];
bool vis[210][1<<13];
priority_queue<pair<pair<int,int>,int>,vector<pair<pair<int,int>,int> >,greater<pair<pair<int,int>,int> > >q;
int n;
int dij()
{
q.push(mp(0,1,0));
memset(d,0x3f,sizeof(d));
d[1][0]=0;
int i,j,s,x,y,z;
while(!q.empty())
{
s=q.top().first.first;
x=q.top().first.second;
y=q.top().second;
q.pop();
if(x==n) return s;
if(vis[x][y]) continue;
vis[x][y]=true;
y|=a[x];
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if((w[i]|y)!=y) continue;
if(d[j][y]>s+t[i])
{
d[j][y]=s+t[i];
q.push(mp(d[j][y],j,y));
}
}
}
return -1;
}
int main()
{
int m,p,k;
scanf("%d%d%d%d",&n,&m,&p,&k);
int i,j,x,y,l,z,o,u;
for(i=1;i<=k;i++)
{
scanf("%d%d",&x,&y);
for(j=1;j<=y;j++)
{
scanf("%d",&z);
a[x]|=(1<<(z-1));
}
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&x,&y,&z,&o);
u=0;
for(j=1;j<=o;j++)
{
scanf("%d",&l);
u|=(1<<(l-1));
}
ae(x,y,z,u);ae(y,x,z,u);
}
printf("%d\n",dij());
}