杭电 6386 AtCoder Regular Contest 061 E Upc 6469 这辈子都学不会的拆点+最短路

9 篇文章 0 订阅
7 篇文章 0 订阅

题目链接:杭电 : http://acm.hdu.edu.cn/showproblem.php?pid=6386

                 Atcode: https://arc061.contest.atcoder.jp/tasks/arc061_c

这个代码打死T杭电,没加输入挂,也没有花Vector,原来堆优化的假算法那种方法在杭电AC了,但是在ATcode是过不去的,所以就有重新写了一个拆点的,但是Atcode过了,杭电T到怀疑了。

就按照在Atcode过的来说一下我的思路吧,本来杭电过的代码去了Atcode  WA大概12组实例,然后百度了下说是拆点建一个新图跑最短路就行了。 

第一次做拆点的题,整理下对于这道题拆点的思路,本来是(U,V,C)这样的一个三元组的关系,本来有公司和路径2个关系,现在将他们拆开,拆成公司的一个对应关系,这样就变成一个不定点了,然后建立(u,c) (v,c)的双向边,权值为1,代表这两个点在这个公司的通路网内,可以再u,v上车,然后对于(u,c) 和 (v,c) 这个关系在建立一个双向边,表示这个公司内部可以通车,这样的话就是不花钱的喽,举个例子

3 2

1 2 1 

2 3 1   

这样建立的边有 (1,4,1)(4,1,1) (2,5,1)(5,2,1)(4,5,0)(5,4,0) 和 (2,5,1) (5,2,1) (3,6,1) (6,3,1)(5,6,0)(6,5,0)这样从1上车通过1公司的内部网格到达(1,4,1) (4,5,0) 然后(5,6,0) (6,3,1) 这样就结束了这个网格,权值最后是2,因为把上车和下车都是权值为1,所以最后输出dist[n] / 2

试了试spfa和堆优化的Dijkstra时间差不多,可能没有去卡这个东西吧。

对于拆点理解可能不太好理解,可以画下图自己感受下。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
typedef pair<ll,ll>Pa;
const long long INF = 1ll<<62;
const int maxn = 5e5+10;
ll n,m,cnt;
map<ll,ll>Ma[maxn];//标记
vector<Pa>V[maxn];//存放pair关系
int vis[maxn];
ll dist[maxn];
struct node{
	ll dist;
	int id;
	bool operator < (const node& b) const
	{
		return dist > b.dist;
	}
};
int getid(int u,int v)//返回编号 
{
	if(Ma[u][v]==0)
		Ma[u][v]=++cnt;
	return Ma[u][v];
} 
inline void spfa()
{
	for(int i=0;i<=cnt;i++)
	{
		vis[i]=0;
		dist[i]=INF;
	}
	node temp;
	temp.dist=0;
	temp.id=1;
	priority_queue<node> q;
	q.push(temp);
	dist[1]=0;
	while(!q.empty())
	{
		node u=q.top();
		q.pop();
		if(vis[u.id]==1)
			continue;
		vis[u.id]=1;
		for(int i=0;i<V[u.id].size();i++)
		{
			int v=V[u.id][i].first;
			int Valu=V[u.id][i].second;
			if(dist[v] > dist[u.id]+Valu)
			{
				dist[v]=dist[u.id]+Valu;
				node Temp;
				Temp.dist=dist[v];
				Temp.id=v;
				q.push(Temp);
			}	
		}		
	}
	if(dist[n]==INF)
		printf("-1\n"); 
	else
		printf("%lld\n",dist[n]/2);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{ 
		for(int i=0;i<=n;i++)
			Ma[i].clear(); 
		for(int i=0;i<=n;i++)
			V[i].clear();
		cnt=n;//从n+1开始记录编号
		for(int i=0;i<m;i++)
		{
			int u,v,id;
			scanf("%d%d%d",&u,&v,&id);
			int uid=getid(u,id);
			int vid=getid(v,id);
			V[u].push_back(Pa(uid,1));
			V[uid].push_back(Pa(u,1));
			
			V[v].push_back(Pa(vid,1));
			V[vid].push_back(Pa(v,1));
			
			V[vid].push_back(Pa(uid,0));
			V[uid].push_back(Pa(vid,0));
		} 
		spfa();
	} 
	return 0;
}
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值