tyvj1467 通向聚会的道路

44 篇文章 0 订阅
32 篇文章 0 订阅
题意:

给定一个有向图;

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

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

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值