题意:
给定一个有向图;
其中有些边为仅有走过点数为 奇数 时才能通行;
其中有些边为仅有走过点数为 偶数 时才能通行;
求几个点到第n号点的路径最小值;
n<=10000,m<=100000
题解:
正着搜在极限数据下必然是超时的,所以显然可以反向建图;
那么这大概就是一个单源最短路的问题;
主要难点就是对于奇偶步数的判断以及维护;
可以考虑将一个点拆成两个,分别表示奇偶;
奇数为x,偶数为x+n;
那么在spfa时的f[x]数组的意义就是:当走到此点时,步数为奇数,到n点的还要走的最短路程;
f[x+n]同理,只不过是偶数的情况;
那么,反向建图的方法也清楚了;
对于正向时,从x到y权值为v的单向边:
仅奇数通行: v为奇数:y+n -> x
v为偶数:y -> x
仅偶数通行: v为奇数:y -> x+n
v为偶数:y+n -> x+n
这样建图就可以搞了,对于每个点的答案,应当是f[x+n];
代码:
#include<queue>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 20001
using namespace std;
struct node
{
int x,y,v;
bool is;
}edge[N*10];
vector<int>to[N],val[N];
queue<int>q;
int f[N];
bool inq[N];
char name[100],ans_name[100];
int main()
{
int n,m,i,j,k,x,y,v,ans=0x3f3f3f3f;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].y,&edge[i].x,&edge[i].v);
scanf("%d",&k);
for(i=1;i<=k;i++)
{
scanf("%d",&j);
to[edge[j].x+(edge[j].v&1?n:0)].push_back(edge[j].y);
val[edge[j].x+(edge[j].v&1?n:0)].push_back(edge[j].v);
edge[j].is=1;
}
scanf("%d",&k);
for(i=1;i<=k;i++)
{
scanf("%d",&j);
to[edge[j].x+(edge[j].v&1?0:n)].push_back(edge[j].y+n);
val[edge[j].x+(edge[j].v&1?0:n)].push_back(edge[j].v);
edge[j].is=1;
}
for(i=1;i<=m;i++)
{
if(edge[i].is) continue;
to[edge[i].x+(edge[i].v&1?n:0)].push_back(edge[i].y);
val[edge[i].x+(edge[i].v&1?n:0)].push_back(edge[i].v);
to[edge[i].x+(edge[i].v&1?0:n)].push_back(edge[i].y+n);
val[edge[i].x+(edge[i].v&1?0:n)].push_back(edge[i].v);
}
memset(f,0x3f,sizeof(f));
f[n]=f[n<<1]=0;
inq[n]=inq[n<<1]=1;
q.push(n);q.push(n<<1);
while(!q.empty())
{
k=q.front(),q.pop();
inq[k]=0;
for(i=0;i<to[k].size();i++)
{
if(f[y=to[k][i]]>f[k]+val[k][i])
{
f[y]=f[k]+val[k][i];
if(inq[y]==0)
{
q.push(y);
inq[y]=1;
}
}
}
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%s",&x,name);
k=f[x+n];
if(k<ans)
ans=k,strcpy(ans_name,name);
else if(k==ans)
if(strcmp(ans_name,name)>0)
strcpy(ans_name,name);
}
printf("%s\n%d\n",ans_name,ans);
return 0;
}