tyvj1467 通向聚会的道路

来源:互联网 发布:sql的查询语句怎么写 编辑:程序博客网 时间:2024/04/30 07:39
题意:

给定一个有向图;

其中有些边为仅有走过点数为 奇数 时才能通行;

其中有些边为仅有走过点数为 偶数 时才能通行;

求几个点到第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 20001using 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;}


0 0
原创粉丝点击