ACM/ICPC 之 SPFA练习两道(ZOJ3088-ZOJ3103)

两道题都需要进行双向SPFA,比范例复杂,代码也较长,其中第二题应该可以用DFS或者BFS做,如果用DFS可能需要的剪枝较多。



ZOJ3088-Easter Holydays

//利用SPFA找出下降最长路径和上升最短路径,输出最大的比值和回路路径
//Time:0Ms	Memory:328K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

#define MAX 1005
#define INF 0x3f3f3f3f

struct Edge {
	int u, w, next;
	Edge(){}
	Edge(int uu,int ww,int nn):u(uu),w(ww),next(nn){}
}eu[MAX], ed[MAX];	//up-down

int n, m, k;
int hu[MAX], hd[MAX];	//邻接表头位置
int pu[MAX], pd[MAX];	//路径
int du[MAX], dd[MAX];	//最短路长
int tu[MAX], td[MAX];	//临时路径
bool v[MAX];

//上升最短路径
void spfa_u(int x)
{
	memset(du, INF, sizeof(du));
	memset(pu, -1, sizeof(pu));
	memset(v, false, sizeof(v));
	du[x] = 0;
	queue<int> q;
	q.push(x);	pu[x] = x;
	while (!q.empty()) {
		int cur = q.front();
		q.pop();	v[cur] = false;
		for (int i = hu[cur]; i != -1; i = eu[i].next)
		{
			int u = eu[i].u, w = eu[i].w;
			if (du[u] > du[cur] + w)
			{
				du[u] = du[cur] + w;
				pu[u] = cur;
				if (!v[u]) {
					v[u] = true; q.push(u);
				}
			}
		}
	}
}

//SPFA-下降最长路径
void spfa_d(int x)
{
	memset(dd, -1, sizeof(dd));
	memset(pd, -1, sizeof(pd));
	memset(v, false, sizeof(v));
	dd[x] = 0;
	queue<int> q;
	q.push(x);	pd[x] = x;
	while (!q.empty()) {
		int cur = q.front();
		q.pop();	v[cur] = false;
		for (int i = hd[cur]; i != -1; i = ed[i].next)
		{
			int u = ed[i].u, w = ed[i].w;
			if (dd[u] < dd[cur] + w)
			{
				dd[u] = dd[cur] + w;
				pd[u] = cur;
				if (!v[u]) {
					v[u] = true; q.push(u);
				}
			}
		}
	}
}

void path_u(int x)
{
	if (tu[x] != x) path_u(tu[x]);
	printf("%d ", x);
}

void path_d(int x)
{
	int i;
	for (i = td[x]; i != td[i]; i = td[i])
		printf("%d ", i);
	printf("%d\n", i);
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		memset(hu, -1, sizeof(hu));
		memset(hd, -1, sizeof(hd));
		scanf("%d%d%d", &n, &m, &k);
		int a, b, w;
		for (int i = 0; i < m; i++)
		{
			scanf("%d%d%d", &a, &b, &w);
			ed[i] = Edge(a, w, hd[b]);	//反向建图
			hd[b] = i;
		}
		for (int i = 0; i < k; i++)
		{
			scanf("%d%d%d", &a, &b, &w);
			eu[i] = Edge(b, w, hu[a]);	//正向建图
			hu[a] = i;
		}

		double rate = 0;
		int tmp = -1;
		for (int i = 1; i <= n; i++)
		{
			spfa_u(i);	spfa_d(i);
			for (int j = 1; j <= n; j++)
			{
				if (i == j || du[j] == INF)	continue;
				if (rate < 1.0 * dd[j] / du[j]) {
					rate = 1.0 * dd[j] / du[j];
					tmp = j;
					memcpy(tu, pu, (n+1)*sizeof(int));
					memcpy(td, pd, (n+1)*sizeof(int));
				}
			}
		}
		path_u(tmp);	path_d(tmp);
		printf("%.3f\n", rate);
	}
	return 0;
}


ZOJ3103-Cliff Climbing

//需要理清题意,比较复杂,建立双向邻接表,并须计算双向最短路
//第一次边表设大了MLE了...考虑最大边数 < n*18个,因此设为MAX*18
//Time:190Ms	Memory:1152K
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

#define MAXW 32
#define MAXH 62
#define MAX MAXW*MAXH
#define INF 0x3f3f3f3f
#define IN_RANGE(x,y) (x >= 0 && x < H && y >= 0 && y < W)

struct Edge {
	int u, w, next;
	Edge() {}
	Edge(int uu, int ww, int nn) :u(uu), w(ww), next(nn) {}
}e[2][MAX*18];	//0:左脚点	1::右脚点

int W, H, n;
int board[MAX];
int h[2][MAX], le[2];
int d[2][MAX];	//双向最短距离
bool v[MAX];

int mov0[9][2] = { { 0, 1 },{0, 2 },{0, 3 },{-1, 1 },{ -1, 2 },{ -2, 1 },{ 1, 1 },{ 1, 2 },{ 2, 1 } };	//左脚踩住,右脚移动位置
int mov1[9][2] = { { 0, -1},{0, -2},{0, -3},{-1, -1}, {-1, -2}, {-2, -1}, {1, -1}, {1, -2}, {2, -1} };	//右脚踩住,左脚移动位置

void spfa(int x)
{
	memset(v, false, sizeof(v));
	memset(d, INF, sizeof(d));
	queue<int> q;
	q.push(x);	d[0][x] = d[1][x] = 0;
	while (!q.empty()) {
		int cur = q.front();
		q.pop();	v[cur] = false;
		for (int k = 0; k < 2; k++)	//双向最短路
			for (int i = h[k][cur]; i != -1; i = e[k][i].next)
			{
				int u = e[k][i].u;
				int w = e[k][i].w;
				if (d[!k][u] > d[k][cur] + w)	//交叉影响
				{
					d[!k][u] = d[k][cur] + w;
					if (!v[u]) {
						v[u] = true; q.push(u);
					}
				}

			}
	}
}

int main()
{
	while (scanf("%d%d", &W, &H), W && H)
	{
		char s[3];
		n = W*H;
		memset(h, -1, sizeof(h));
		//一维序列表示各点
		for (int i = 0; i < n; i++)
		{
			scanf("%s", s);
			if (s[0] == ‘S‘ || s[0] == ‘T‘)	board[i] = 0;
			else if (s[0] == ‘X‘)	board[i] = INF;
			else board[i] = s[0] - ‘0‘;
		}

		//构建邻接表
		le[0] = le[1] = 0;
		for (int i = 0; i < n; i++)
		{
			if ((i < W && board[i] == 0) || board[i] == INF) continue;
			for (int j = 0; j < 9; j++)
			{
				int x = i / W, y = i % W;	//计算行与列
				int x0 = x + mov0[j][0], y0 = y + mov0[j][1];
				int x1 = x + mov1[j][0], y1 = y + mov1[j][1];
				int n0 = x0*W + y0, n1 = x1*W + y1;
				if (IN_RANGE(x0,y0) && board[n0] != INF) {
					e[0][le[0]] = Edge(n0, board[n0], h[0][i]);
					h[0][i] = le[0]++;
				}
				if (IN_RANGE(x1,y1) && board[n1] != INF) {
					e[1][le[1]] = Edge(n1, board[n1], h[1][i]);
					h[1][i] = le[1]++;
				}
			}
		}

		int Min = INF;
		for (int i = (H - 1) * W; i < n; i++)	//枚举最后一行‘S‘进行SPFA
			if (board[i] == 0)
			{
				spfa(i);
				for (int j = 0; j < W; j++)	//遍历第一行‘T‘的最短路长
					if(board[j] == 0)	Min = min(min(Min, d[0][j]), d[1][j]);
			}
		if (Min == INF)	Min = -1;
		printf("%d\n", Min);
	}
	return 0;
}
时间: 2024-11-15 06:24:14

ACM/ICPC 之 SPFA练习两道(ZOJ3088-ZOJ3103)的相关文章

ACM/ICPC 之 SPFA范例两道(POJ3268-POJ3259)

两道以SPFA算法求解的最短路问题,比较水,第二题需要掌握如何判断负权值回路. POJ3268-Silver Cow Party //计算正逆最短路径之和的最大值 //Time:32Ms Memory:360K #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<algorithm> using namespace std; #define

ACM/ICPC 之 差分约束系统两道(ZOJ2770-POJ1201)

当对问题建立数学模型后,发现其是一个差分方程组,那么问题可以转换为最短路问题,一下分别选用Bellmanford-SPFA解题 ZOJ2770-Burn the Linked Camp //差分约束方程组-转换为最短路问题 //d[v] <= d[u] + dis[u][v] -> d[v] - d[u] <= dis[u][v] //Time:110Ms Memory:12116 #include<iostream> #include<cstring> #inc

ACM/ICPC 之 两道dijkstra练习题(ZOJ1053(POJ1122)-ZOJ1053)

两道较为典型的单源最短路径问题,采用dijkstra解法 本来是四道练习题,后来发现后面两道用dijkstra来解的话总觉得有点冗余了,因此暂且分成三篇博客(本篇以及后两篇). ZOJ1053(POJ1122)-FDNY to the Rescue! 1 //POJ1122-ZOJ1053 2 //dijkstra-需要记录路径 3 //给出n个路口的邻接矩阵,求给定多个火警到失火点的时间及任一路径 4 //注意输入最后一行时,cin.getline需要两次,猜测需要接受邻接矩阵最后一行其他字符

ACM/ICPC 之 欧拉回路两道(POJ1300-POJ1386)

两道有关欧拉回路的例题 POJ1300-Door Man //判定是否存在从某点到0点的欧拉回路 //Time:0Ms Memory:116K #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAX 25 int st, n; int door[MAX]; int main() { char s[120]; while (scanf("%s&q

2017 ACM/ICPC Asia Regional Shenyang Online spfa+最长路

transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 1496    Accepted Submission(s): 723 Problem Description Kelukin is a businessman. Every day, he travels arou

【转】[email&#160;protected]&#183;ACM/ICPC 回忆录

转自:http://hi.baidu.com/ordeder/item/2a342a7fe7cb9e336dc37c89 2009年09月06日 星期日 21:55 初识ACM最早听说ACM/ICPC这项赛事是在大三上的算法课上张老师提到的,当时我们学校的组织参加这项活动才刚刚起步,我也没太在意,总觉得那是非常遥远的事,事实上当时我也从未相当如今我们能获得现在的成绩.真正踏入ACM/ICPC这个神奇的世界,不得不提到2004那一年我们学校的参赛队伍xmutank,正是听了pipo师兄的精彩演讲以

[转]浅谈ACM ICPC的题目风格和近几年题目的发展

斯坦福大学 王颖 ACM ICPC的比赛形式一般是五个小时八个题目,综合考察选手的数学能力.算法能力.coding能力和debug能力,还有团队配合能力.数学方面主要强调组合数学.图论和数论这三个方面的能力:而算法的覆盖范围很广,涉及了大部分经典的算法,和少量较前沿的算法.由于每道题目都需要通过所有的测试数据才能得分,并且需要精确解,这限制了Approximation algorithm在一些NP-hard的题目中的运用,从而使得搜索和剪枝策略对于NP-hard的题目非常重要. Final的题目

ACM/ICPC 之 数据结构-邻接表+DP+队列+拓扑排序(TshingHua OJ-旅行商TSP)

做这道题感觉异常激动,因为在下第一次接触拓扑排序啊= =,而且看了看解释,猛然发现此题可以用DP优化,然后一次A掉所有样例,整个人激动坏了,哇咔咔咔咔咔咔咔~ 咔咔~哎呀,笑岔了- -|| 旅行商(TSP) Description Shrek is a postman working in the mountain, whose routine work is sending mail to n villages. Unfortunately, road between villages is

《ACM/ICPC 算法训练教程》读书笔记一之数据结构(堆)

书籍简评:<ACM/ICPC 算法训练教程>这本书是余立功主编的,代码来自南京理工大学ACM集训队代码库,所以小编看过之后发现确实很实用,适合集训的时候刷题啊~~,当时是听了集训队final的意见买的,感觉还是不错滴. 相对于其他ACM书籍来说,当然如书名所言,这是一本算法训练书,有着大量的算法实战题目和代码,尽管小编还是发现了些许错误= =,有部分注释的语序习惯也有点不太合我的胃口.实战题目较多是比较水的题,但也正因此才能帮助不少新手入门,个人认为还是一本不错的算法书,当然自学还是需要下不少