POJ 3592 Instantaneous Transference Tarjan+SPFA

题目大意:给出一张地图,有数字的点代表上面有数字个矿物,*代表这个点可以传送到另一个点上,#代表不能走。从一个点只能到这个点的下方和右方。现在从(0,0)开始,问最多可以收集多少矿物。

思路:这个题肯定是建图,然后最长路,关键是有了传送,就有可能形成正权环,然后在SPFA的过程中就会死循环。一个环上的所有权值只能得到一次,所以就用一次Tarjan求出所有的环,把权值累计一下,变成一个点,然后重新建图。这样得到的图就是拓扑图了,没有环,可以无忧无虑的SPFA了。

PS:这个题我用拓扑排序怎么也切不了,求大神告知这是为什么!!!

CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 20010
using namespace std;

int cases;
int m,n;
char src[50][50];
int transmit[MAX],points;

int head[MAX],total;
int next[MAX],aim[MAX];

int dfn[MAX],low[MAX],_clock;
int stack[MAX],top;
bool in_stack[MAX];
int changed[MAX],scc,val[MAX];
int a[MAX];

int _head[MAX],_total;
int _next[MAX],_aim[MAX];

int f[MAX];
bool v[MAX];

inline void Initialize()
{
	total = _total = _clock = top = points = scc = 0;
	memset(head,0,sizeof(head));
	memset(_head,0,sizeof(_head));
	memset(dfn,0,sizeof(dfn));
	memset(in_stack,false,sizeof(in_stack));
	memset(val,0,sizeof(val));
}

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

inline void _Add(int x,int y)
{
	_next[++_total] = _head[x];
	_aim[_total] = y;
	_head[x] = _total;
}

void Tarjan(int x)
{
	dfn[x] = low[x] = ++_clock;
	in_stack[x] = true;
	stack[++top] = x;
	for(int i = head[x]; i; i = next[i]) {
		if(!dfn[aim[i]])
			Tarjan(aim[i]),low[x] = min(low[x],low[aim[i]]);
		else if(in_stack[aim[i]])
			low[x] = min(low[x],dfn[aim[i]]);
	}
	if(dfn[x] == low[x]) {
		scc++;
		int temp;
		do {
			temp = stack[top--];
			changed[temp] = scc;
			in_stack[temp] = false;
			val[scc] += a[temp];
		}while(temp != x);
	}
}

inline void SPFA()
{
	static queue<int> q;
	while(!q.empty()) 	q.pop();
	memset(f,0,sizeof(f));
	memset(v,false,sizeof(v));
	f[changed[0]] = val[changed[0]];
	q.push(changed[0]);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		v[x] = false;
		for(int i = _head[x]; i; i = _next[i])
			if(f[_aim[i]] < f[x] + val[_aim[i]]) {
				f[_aim[i]] = f[x] + val[_aim[i]];
				if(!v[_aim[i]]) {
					v[_aim[i]] = true;
					q.push(_aim[i]);
				}
			}
	}
}

int main()
{
	for(cin >> cases; cases; --cases) {
		scanf("%d%d",&n,&m);
		Initialize();
		for(int i = 0; i < n; ++i)
			scanf("%s",src[i]);
		for(int i = 0; i < n; ++i)
			for(int x,y,j = 0;j < m; ++j)
				if(src[i][j] == '*') {
					scanf("%d%d",&x,&y);
					a[i * m + j] = 0;
					if(src[x][y] != '#')
						Add(i * m + j,x * m + y);
					if(i + 1 < n && src[i + 1][j] != '#')
						Add(i * m + j,(i + 1) * m + j);
					if(j + 1 < m && src[i][j + 1] != '#')
						Add(i * m + j,i * m + j + 1);
				}
				else if(src[i][j] != '#') {
					a[i * m + j] = src[i][j] - '0';
					if(i + 1 < n && src[i + 1][j] != '#')
						Add(i * m + j,(i + 1) * m + j);
					if(j + 1 < m && src[i][j + 1] != '#')
						Add(i * m + j,i * m + j + 1);
				}
		for(int i = 0; i < m * n; ++i)
			if(!dfn[i])	Tarjan(i);
		for(int x = 0; x < m * n; ++x)
			for(int i = head[x]; i; i = next[i])
				if(changed[x] != changed[aim[i]])
					_Add(changed[x],changed[aim[i]]);
		SPFA();
		int *ans = max_element(f + 1,f + scc + 1);
		printf("%d\n",*ans);
	}
	return 0;
}

时间: 2024-08-28 02:11:19

POJ 3592 Instantaneous Transference Tarjan+SPFA的相关文章

poj 3592 Instantaneous Transference 强连通图 缩点 再求最长路

1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #include<stack> 5 #include<queue> 6 using namespace std; 7 #define maxx 44 8 #define maxx2 44*44 9 #define INF 99999999 10 char s[maxx][maxx]; 11 bool tong[maxx2

POJ 3592 Instantaneous Transference(强连通+DP)

POJ 3592 Instantaneous Transference 题目链接 题意:一个图,能往右和下走,然后有*可以传送到一个位置,'#'不能走,走过一个点可以获得该点上面的数字值,问最大能获得多少 思路:由于有环先强连通缩点,然后问题转化为dag,直接dp即可 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <sta

POJ 3592 Instantaneous Transference(建图强连通+单源最长路)

题目大意:有一张n*m的地图,每个点上可能是数字,代表矿石的数目,可能是*,表示一个传送阵,送往某个坐标,可能是#,代表不通.每次矿车只能往右方或者下方走一格,问从(0,0)点出发可以最多收集到多少矿石 思路:先根据矿车的可移动的方向建有向图,"*"导致可能会有环,所以先缩点变成有向无环图. 然后就是DAG上的最长路问题(拓扑排序+dp) 而且也是单源最长路问题,可以用最短路算法去做 吐槽一下debug了一天,原来是tarjan的时间戳写跪了,关键是直到现在仍不觉得那种写法是错的,对拍

poj3592 Instantaneous Transference tarjan缩点+建图

//给一个n*m的地图,坦克从(0 , 0)开始走 //#表示墙不能走,*表示传送门可以传送到指定地方,可以选择也可以选择不传送 //数字表示该格的矿石数, //坦克从(0,0)开始走,只能往右和往下走, //问最多能得到多少矿石 //直接建图,但由于有传送门,需要缩点 //然后用dfs直接搜一条权值最大的路 #include<cstdio> #include<cstring> #include<iostream> #include<vector> #inc

【连通图|强连通分量+最长路】POJ-3592 Instantaneous Transference

Instantaneous Transference Time Limit: 5000MS Memory Limit: 65536K Description It was long ago when we played the game Red Alert. There is a magic function for the game objects which is called instantaneous transfer. When an object uses this magic fu

POJ 3169 Layout (差分约束+SPFA)

Layout Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6832   Accepted: 3292 Description Like everyone else, cows like to stand close to their friends when queuing for feed. FJ has N (2 <= N <= 1,000) cows numbered 1..N standing along a

POJ 3268 Silver Cow Party(SPFA)

Description One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1..N is going to attend the big cow party to be held at farm #X (1 ≤ X ≤ N). A total of M (1 ≤ M ≤ 100,000) unidirectional (one-way roads connects pairs of farms; road i re

poj 1860 Currency Exchange (SPFA、正权回路 bellman-ford)

链接:poj 1860 题意:给定n中货币,以及它们之间的税率,A货币转化为B货币的公式为 B=(V-Cab)*Rab,其中V为A的货币量, 求货币S通过若干此转换,再转换为原本的货币时是否会增加 分析:这个题就是判断是否存在正权回路,可以用bellman-ford算法,不过松弛条件相反 也可以用SPFA算法,判断经过转换后,转换为原本货币的值是否比原值大... bellman-ford    0MS #include<stdio.h> #include<string.h> str

bzoj 1179[Apio2009]Atm (tarjan+spfa)

题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 输出 输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数. 样例输入 6 7 1