方法:最短路预处理+状压DP
解析:
看到k=20就想要压一压。
(这种病怎么治!
然后想后来的q个限制。
如果一个点可以被经过的话,那么它的前置点必须都走过。
所以我们要对每个数搞一个压前置点的二进制数。
然后最短路预处理出那k个点以及1号点到任意点的最短路。
状态转移即可。
转移因为枚举不适合,无法做,所以直接上个记忆化就好了。
这题卡常数真的好么 = =
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20010
#define M 200100
#define INF 0x3f3f3f3f
using namespace std;
int head[N];
int cnt;
int v[N];
int n,m,k;
int dis[22][1<<20];
int dp[22][1<<20];
int limit[N];
struct node
{
int from,to,val,next;
}edge[M<<1];
void init()
{
memset(dp,-1,sizeof(dp));
memset(dis,0x3f,sizeof(dis));
memset(head,-1,sizeof(head));
cnt=1;
}
void edgeadd(int from,int to,int val)
{
edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
edge[cnt].next=head[from],head[from]=cnt++;
}
struct element
{
int no,val;
};
bool operator < (element a,element b)
{
if(a.val==b.val)return a.no<b.no;
return a.val>b.val;
}
void dijikstra(int s)
{
memset(v,0,sizeof(v));
priority_queue<element>q;
element fir;
fir.no=s,fir.val=0;
q.push(fir);
dis[s][s]=0;
while(!q.empty())
{
element u=q.top();
q.pop();
if(v[u.no])continue;
v[u.no]=1;
for(int i=head[u.no];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(u.val+edge[i].val<dis[s][to])
{
dis[s][to]=u.val+edge[i].val;
if(!v[to])
{
element tmp;
tmp.no=to,tmp.val=dis[s][to];
q.push(tmp);
}
}
}
}
dis[s][k+2]=dis[s][n];
}
int dfs(int now,int status)
{
if(dp[now][status]>=0)return dp[now][status];
if(status==((1<<k)-1))return dis[now][k+2];
dp[now][status]=INF;
for(int i=2;i<=k+1;i++)
{
if((status&limit[i])==limit[i])
{
dp[now][status]=min(dp[now][status],dis[now][i]+dfs(i,status|(1<<(i-2))));
}
}
return dp[now][status];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
init();
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
edgeadd(x,y,z);
edgeadd(y,x,z);
}
for(int i=1;i<=k+1;i++)
{
dijikstra(i);
}
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
limit[y]|=(1<<(x-2));
}
printf("%d\n",dfs(1,0));
}