【最短路】解题报告

4.最短路(path.c/cpp)

[问题描述]

给定一个包含N个点,M条边的无向图,每条边的边权均为1。

再给定K个三元组(A,B,C),表示从A点走到B点后不能往C点走。注意三元组是有序的,如可以从B点走到A点再走到C。

现在你要在K个三元组的限制下,找出1号点到N号点的最短路径,并输出任意一条合法路径,会有Check检查你的输出。

[输入格式]

输入文件第一行有三个数N,M,K,意义如题目所述。

接下来M行每行两个数A,B,表示A,B间有一条边。

再下面K行,每行三个数(A,B,C)描述一个三元组。

[输出格式]

输出文件共两行数,第一行一个数S表示最短路径长度。

第二行S+1个数,表示从1到N所经过的节点。

[样例输入]

4 4 2

1 2

2 3

3 4

1 3

1 2 3

1 3 4

[样例输出]

4

1 3 2 3 4

[数据范围]

对于40%的数据满足N≤10,M≤20,K≤5。

对于100%的数据满足N≤3000,M≤20000,K≤100000。

 


这道题是一道拆点的题,不过也可以用广搜来做,每次记录父亲就行了。

一开始我就卡在了这点上,走到下一个点,但是并不知道这个状态是由哪一个父亲派生出来的,但是其实就是这么简单,对每个队列中的元素都记录父亲。


广搜:

#include <cstdio>


struct node
{
	long y;
	long z;
	node* next;
};
node* g[3002][3002];


long n;long m;long k;
long que[1100000];
long que2[1100000];
long fa[3002];
bool map[3002][3002];


void insert(long x,long z,long y)
{
	node* tmp = new node;
	tmp -> z = z;
	tmp -> y = y;
	tmp -> next = g[x][y];
	g[x][y] = tmp;
}


void output(long p)
{
	if (fa[p]==0)
	{
		printf("%ld ",que[p]);
		return;
	}
	output(fa[p]);
	printf("%ld ",que[p]);	
}


bool check(long a,long b)
{
	node* ths = g[fa[a]][b];
	
	while (ths)
	{
		if (ths->z==a) return false;
		ths = ths->next;
	}
	return true;
}


void bfs()
{
	long l= 0;long r= 0;
	r++;
	que[r] = 1;
	while (l<r)
	{
		long now = que[++l];
		for (long i=1;i<n+1;i++)
		{
			if (now == i)continue;
			if (map[now][i]&&check(l,i))
			{
				map[now][i] = false;
				r++;
				fa[r] = l;
				que[r] = i;
				que2[r] = que2[l]+1; 
				if (i==n)
				{
					printf("%ld\n",que2[r]);
					output(r);
					return;
				}
			}
		}
	}
}


int main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	scanf("%ld%ld%ld",&n,&m,&k);
	for (long i=1;i<m+1;i++)
	{
		long a;long b;
		scanf("%ld%ld",&a,&b);
		map[a][b] = true;
		map[b][a] = true;
	}	
	for (long i=1;i<k+1;i++)
	{
		long a;long b;long c;
		scanf("%ld%ld%ld",&a,&b,&c);
		insert(a,b,c);
	}
	bfs(); 
	return 0;
}


spfa+拆点

意思容易理解,只是代码不好写,因为结构体太多了。容易混淆

拆点关键就在于,对于hash、dist、还有记录方案的g统统都要增加一维。

上个方案的bfs中,hash增加了一维也是基于这个原因,因为不能从上一个点到这个点来了,但是还会有其他的路到这个点来

#include <cstdio>

struct node
{
	long z;
	node* next;
};
node* g[3002][3002];

struct node1
{
	long index;
	node1* next;
};

struct tnode
{
	long f;
	long i;
};

long n;long m;long k;
tnode que[100000];
bool map[3002][3002];
node1* dian[3002];
bool used[3002][3002];
long dist[3002][3002];
long fangan[3002][3002];

void insert1(long x,long y)
{
	node1* tmp = new node1;
	tmp->index = y;
	tmp->next = dian[x];
	dian[x] = tmp;
}

void insert(long x,long y,long z)
{
	node* tmp = new node;
	tmp -> z = z;
	tmp -> next = g[x][y];
	g[x][y] = tmp;
}

bool can(long a,long b,long c)
{
	node* ths = g[a][b];
	
	while (ths)
	{
		if (ths->z==c) return false;
		ths = ths->next;
	}
	return true;
}

void spfa()
{
	for (long i=0;i<n+1;i++)
	{
		for (long j=0;j<n+1;j++)
		{
			dist[i][j] = 0x7fff0000;
		}
	}
	used[1][1] = true;
	dist[1][1] = 0;
	long l = 0;
	long r = 1;
	que[r].f=1;
	que[r].i=1;
	while (l<r)
	{
		l++;
		tnode now = que[l];
		used[now.f][now.i] = false;
		node1* ths = dian[now.i];
		while (ths)
		{
			if (can(now.f,now.i,ths->index)&&dist[now.i][ths->index]>dist[now.f][now.i]+1)
			{
				dist[now.i][ths->index]=dist[now.f][now.i]+1;
				fangan[now.i][ths->index] = l;
				if (!used[now.i][ths->index])
				{
					used[now.i][ths->index] = true;
					r++;
					que[r].i = ths->index;
					que[r].f = now.i;
				}
			}
			ths = ths -> next;
		}
	}
}	

void output(long a,long b)
{
	if(b==1)
	{
		printf("1 ");
		return;
	}
	tnode* la = &que[fangan[a][b]];
	output(la->f,la->i);
	printf("%ld ",b);
}	

int main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	scanf("%ld%ld%ld",&n,&m,&k);
	for (long i=1;i<m+1;i++)
	{
		long a;long b;
		scanf("%ld%ld",&a,&b);
		insert1(a,b);
		insert1(b,a);
	}	
	for (long i=1;i<k+1;i++)
	{
		long a;long b;long c;
		scanf("%ld%ld%ld",&a,&b,&c);
		insert(a,b,c);
	}
	spfa();
	long ans = 0x7fff0000;
	long t = 0;
  	for (long i=1;i<n;i++)
  	{
		if (dist[i][n]<ans)
  		{
		    ans=dist[i][n];
		    t=i;
		}
	}
  	printf("%ld\n",ans);
  	output(t,n);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值