poj 3592 Instantaneous Transference 【SCC +缩点 + SPFA】
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 6204 | Accepted: 1389 |
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 function, it will be transferred to the specified point immediately, regardless of how
far it is.
Now there is a mining area, and you are driving an ore-miner truck. Your mission is to take the maximum ores in the field.
The ore area is a rectangle region which is composed by n × m small squares, some of the squares have numbers of ores, while some do not. The ores can't be regenerated after taken.
The starting position of the ore-miner truck is the northwest corner of the field. It must move to the eastern or southern adjacent square, while it can not move to the northern or western adjacent square. And some squares have magic power that can instantaneously
transfer the truck to a certain square specified. However, as the captain of the ore-miner truck, you can decide whether to use this magic power or to stay still. One magic power square will never lose its magic power; you can use the magic power whenever
you get there.
Input
The first line of the input is an integer T which indicates the number of test cases.
For each of the test case, the first will be two integers N, M (2 ≤ N, M ≤ 40).
The next N lines will describe the map of the mine field. Each of the N lines will be a string that contains M characters. Each character will be an integer X (0 ≤ X ≤ 9) or a '*' or a '#'. The integer X indicates
that square hasX units of ores, which your truck could get them all. The '*' indicates this square has a magic power which can transfer truck within an instant. The '#' indicates this square is full of rock and the truck can't move on this square.
You can assume that the starting position of the truck will never be a '#' square.
As the map indicates, there are K '*' on the map. Then there follows K lines after the map. The next K lines describe the specified target coordinates for the squares with '*', in the order from north to south then west to east.
(the original point is the northwest corner, the coordinate is formatted as north-south, west-east, all from 0 to N - 1,M - 1).
Output
For each test case output the maximum units of ores you can take.
Sample Input
1
2 2
11
1*
0 0
Sample Output
3
拦着我,我要跳馨月湖!
!。
题意:有一个N*M的地图。地图中有三种字符。假设是数字X(0~9),则表示该区域有X的矿物。假设是"*",则表示该区域能够传送到某一个确定位置。
假设是"#",则表示该区域不能进入。如今出发点在地图左上方即(0,0)点,题目保证起点一定不是"#"区域。
要求每次仅仅能向右走、向下走,当遇到传送点的时候能够选择传送到指定位置或者不传送。'*'位置的传送效果能够无限使用,问你从起点出发最多能採多少矿。
思路:
1,把N*M个位置虚拟成N*M个点。依据是否可达的关系建边 来构图。
2,对构成的图求SCC。并统计全部SCC里面的矿物数;
3,缩点后,构建新图。以起点所在的SCC为源点。SPFA求最长路。
注意:
1,'*'的传送位置可能是'#'(题目说了传送位置是合法的。所以传送位置不用考虑是否越界)。并且对于'*'位置,能够选择传送或者不传送。所以必需要向下、右建边(需要考虑越界)。
2。The next K lines
describe the specified target coordinates for the squares with '*', in the order from north to south then west to east. 对于出现的'*'。先逐行遍历,再逐列遍历。
好想的思路,好写的代码。不好理解的——两个实现过程为什么一个WA,一个AC。
不知道这两个实现最长路的建图 有什么不同,我算是无语了。 O__O "… 明天再好好想吧,今天有点不舒服。。
。
希望路过的大牛指点迷津。
代码一:
WA了, 我只是设了一个超级源点连通起点所在的SCC,边权为当前SCC的矿物数。
然后缩点建图时边权为终点SCC的矿物数。这样跑SPFA就WA。 曾经写POJ 3160用这样的写法就AC了。
struct Node//用于跑SPFA
{
int from, to, val, next;
};
Node node[MAXM];
int Head[MAXN], nodenum;
int val[MAXN];//记录每一个SCC的 矿物数
void addNode(int u, int v, int w)
{
Node E = {u, v, w, Head[u]};
node[nodenum] = E;
Head[u] = nodenum++;
}
void suodian()
{
nodenum = 0;
memset(Head, -1, sizeof(Head));
memset(val, 0, sizeof(val));
//求出每一个SCC里面全部的矿物数
for(int i = 1; i <= scc_cnt; i++)
{
int sum = 0;
for(int j = 0; j < scc[i].size(); j++)
sum += num[scc[i][j]];
val[i] = sum;
}
//保证从左上角出发,虚拟源点0 连通原图中起点所在的SCC 边权为当前SCC的矿物数
addNode(0, sccno[0], val[sccno[0]]);
for(int i = 0; i < edgenum; i++)
{
int u = sccno[edge[i].from];
int v = sccno[edge[i].to];
if(u != v)
addNode(u, v, val[v]);//建边
}
}
int dist[MAXN];
bool vis[MAXN];
void SPFA(int sx)//跑一遍SPFA 求最长路
{
queue<int> Q;
memset(dist, 0, sizeof(dist));
memset(vis, false, sizeof(vis));
vis[sx] = true;
Q.push(sx);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = Head[u]; i != -1; i = node[i].next)
{
Node E = node[i];
if(dist[E.to] < dist[u] + E.val)
{
dist[E.to] = dist[u] + E.val;
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
sort(dist, dist+scc_cnt+1);
printf("%d\n", dist[scc_cnt]);
}
代码二:
AC代码的 —— 仅仅建边 不设置边权,用数组记录SCC的矿物数,借此来更新。
struct Node//用于跑SPFA
{
int from, to, next;
};
Node node[MAXM];
int Head[MAXN], nodenum;
int val[MAXN];//记录每一个SCC的 矿物数
void addNode(int u, int v)
{
Node E = {u, v,Head[u]};
node[nodenum] = E;
Head[u] = nodenum++;
}
void suodian()
{
nodenum = 0;
memset(Head, -1, sizeof(Head));
memset(val, 0, sizeof(val));
//求出每一个SCC里面全部的矿物数
for(int i = 1; i <= scc_cnt; i++)
{
int sum = 0;
for(int j = 0; j < scc[i].size(); j++)
sum += num[scc[i][j]];
val[i] = sum;
}
for(int i = 0; i < edgenum; i++)
{
int u = sccno[edge[i].from];
int v = sccno[edge[i].to];
if(u != v)
addNode(u, v);//建边
}
}
int dist[MAXN];
bool vis[MAXN];
void SPFA()//跑一遍SPFA 求最长路
{
queue<int> Q;
memset(dist, 0, sizeof(dist));
memset(vis, false, sizeof(vis));
vis[sccno[0]] = true;
dist[sccno[0]] = val[sccno[0]];
Q.push(sccno[0]);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = Head[u]; i != -1; i = node[i].next)
{
Node E = node[i];
if(dist[E.to] < dist[u] + val[E.to])
{
dist[E.to] = dist[u] + val[E.to];
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
sort(dist, dist+scc_cnt+1);
printf("%d\n", dist[scc_cnt]);
}
没想通o(╯□╰)o
AC代码:0ms
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 2000+10
#define MAXM 3000000+10
#define INF 0x3f3f3f
using namespace std;
struct Edge
{
int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int low[MAXN], dfn[MAXN];
int dfs_clock;
int sccno[MAXN], scc_cnt;
stack<int> S;
vector<int> scc[MAXN];
bool Instack[MAXN];
int N, M;
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
int point(int x, int y)
{
return x * M + y;//这里写反了 RE2次。。。 }
void addEdge(int u, int v)
{
Edge E = {u, v, head[u]};
edge[edgenum] = E;
head[u] = edgenum++;
}
bool judge(int x, int y)//推断越界 写多了。。。
{
return x >= 0 && x < N && y >= 0 && y < M;
}
int num[MAXN];//存储相应位置的 矿物数目
void getMap()
{
scanf("%d%d", &N, &M);
int move[2][2] = {0,1, 1,0};
char str[50][50];
int x[MAXN], y[MAXN];
memset(num, 0, sizeof(num));
int k = 0;
for(int i = 0; i < N; i++)
{
scanf("%s", str[i]);
for(int j = 0; j < M; j++)
if(str[i][j] == '*')
k++;
}
for(int i = 0; i < k; i++)
scanf("%d%d", &x[i], &y[i]);
k = 0;
for(int i = 0; i < N; i++)//注意这里 先遍历行 再遍历列
{
for(int j = 0; j < M; j++)
{
if(str[i][j] == '#')//不能走
continue;
for(int p = 0; p < 2; p++)//两个方向
{
int a = i + move[p][0];
int b = j + move[p][1];
if(judge(a, b) && str[a][b] != '#')//能走且不能越界 连通
addEdge(point(i, j), point(a, b));
}
if(str[i][j] != '*')//数字 有矿物
num[point(i, j)] = str[i][j] - '0';//记录矿物数
else//连通 传送位置
{
if(str[x[k]][y[k]] != '#')
addEdge(point(i, j), point(x[k], y[k]));//由于这里[k++] WA N次
k++;
}
}
}
}
void tarjan(int u, int fa)
{
int v;
low[u] = dfn[u] = ++dfs_clock;
S.push(u);
Instack[u] = true;
for(int i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].to;
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if(Instack[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
scc_cnt++;
scc[scc_cnt].clear();
for(;;)
{
v = S.top(); S.pop();
Instack[v] = false;
sccno[v] = scc_cnt;
sumval[scc_cnt] += num[v];
scc[scc_cnt].push_back(v);
if(v == u) break;
}
}
}
void find_cut(int l, int r)
{
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(sccno, 0, sizeof(sccno));
memset(sumval, 0, sizeof(sumval));
memset(Instack, false, sizeof(Instack));
dfs_clock = scc_cnt = 0;
for(int i = l; i <= r; i++)
if(!dfn[i]) tarjan(i, -1);
}
struct Node//用于跑SPFA
{
int from, to, next;
};
Node node[MAXM];
int Head[MAXN], nodenum;
int val[MAXN];//记录每一个SCC的 矿物数
void addNode(int u, int v)
{
Node E = {u, v,Head[u]};
node[nodenum] = E;
Head[u] = nodenum++;
}
void suodian()
{
nodenum = 0;
memset(Head, -1, sizeof(Head));
memset(val, 0, sizeof(val));
//求出每一个SCC里面全部的矿物数
for(int i = 1; i <= scc_cnt; i++)
{
int sum = 0;
for(int j = 0; j < scc[i].size(); j++)
sum += num[scc[i][j]];
val[i] = sum;
}
//为什么这样写 会WA??? 坑死我了
//保证从左上角出发。虚拟源点0 连通原图中起点所在的SCC 边权为当前SCC的矿物数
//addNode(0, sccno[0], val[sccno[0]]);
for(int i = 0; i < edgenum; i++)
{
int u = sccno[edge[i].from];
int v = sccno[edge[i].to];
if(u != v)
addNode(u, v);//建边
}
}
int dist[MAXN];
bool vis[MAXN];
void SPFA()//跑一遍SPFA 求最长路
{
queue<int> Q;
memset(dist, 0, sizeof(dist));
memset(vis, false, sizeof(vis));
vis[sccno[0]] = true;
dist[sccno[0]] = val[sccno[0]];
Q.push(sccno[0]);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = Head[u]; i != -1; i = node[i].next)
{
Node E = node[i];
if(dist[E.to] < dist[u] + val[E.to])
{
dist[E.to] = dist[u] + val[E.to];
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
sort(dist, dist+scc_cnt+1);
printf("%d\n", dist[scc_cnt]);
}
void solve()
{
find_cut(0, N*M-1);
suodian();
SPFA();
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
init();
getMap();
solve();
}
return 0;
}
poj 3592 Instantaneous Transference 【SCC +缩点 + SPFA】的更多相关文章
- POJ 3592 Instantaneous Transference(强连通+DP)
POJ 3592 Instantaneous Transference 题目链接 题意:一个图.能往右和下走,然后有*能够传送到一个位置.'#'不能走.走过一个点能够获得该点上面的数字值,问最大能获得 ...
- poj 3592 Instantaneous Transference 缩点+最长路
题目链接 给一个n*m的图, 从0, 0这个点开始走,只能向右和向下. 图中有的格子有值, 求能获得的最大值. 其中有些格子可以传送到另外的格子, 有些格子不可以走. 将图中的每一个格子都看成一个点, ...
- POJ 3592 Instantaneous Transference(强联通分量 Tarjan)
http://poj.org/problem?id=3592 题意 :给你一个n*m的矩阵,每个位置上都有一个字符,如果是数字代表这个地方有该数量的金矿,如果是*代表这个地方有传送带并且没有金矿,可以 ...
- poj 3592 Instantaneous Transference
http://poj.org/problem?id=3592 #include <cstdio> #include <cstring> #include <algorit ...
- bzoj 1179 [Apio2009]Atm——SCC缩点+spfa
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1179 显然SCC缩点. 然后准备倒着拓扑序推到st,结果WA. 听TJ说dj求最长路会发生不 ...
- POJ 2186 Popular cows(SCC 缩点)
Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10, ...
- P3119 [USACO15JAN]草鉴定[SCC缩点+SPFA]
题目描述 约翰有n块草场,编号1到n,这些草场由若干条单行道相连.奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草. 贝西总是从1号草场出发,最后回到1号草场.她想经过尽可能多的草场,贝西 ...
- poj3592 Instantaneous Transference tarjan缩点+建图
//给一个n*m的地图.坦克从(0 , 0)開始走 //#表示墙不能走,*表示传送门能够传送到指定地方,能够选择也能够选择不传送 //数字表示该格的矿石数, //坦克从(0,0)開始走.仅仅能往右和往 ...
- POJ 3592--Instantaneous Transference【SCC缩点新建图 && SPFA求最长路 && 经典】
Instantaneous Transference Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 6177 Accep ...
随机推荐
- ubantu16.04安装配置samba服务(原创)
1.安装samba服务 $ sudo apt-get install samba samba-common$ sudo apt-get install smbclient 如果你开启了防火墙,关闭: ...
- 在servlet中返回json数据
在servlet: String name = new tring(request.getParameter("name").getBytes("iso8859-1&qu ...
- 洛谷 P1598 垂直柱状图【字符串】
题目描述 写一个程序从输入文件中去读取四行大写字母(全都是大写的,每行不超过72个字符),然后用柱状图输出每个字符在输入文件中出现的次数.严格地按照输出样例来安排你的输出格式. 输入输出格式 输入格式 ...
- hdu6162(树链剖分)
hdu6162 题意 给出一颗带点权的树,每次询问一对节点 \((u, v)\),问 \(u\) 到 \(v\) 的最短路径上所有节点权值在 \([c1, c2]\) 区间内的和. 分析 树链剖分,那 ...
- 简单DP【p1934】封印
Description 很久以前,魔界大旱,水井全部干涸,温度也越来越高.为了拯救居民,夜叉族国王龙溟希望能打破神魔之井,进入人界"窃取"水灵珠,以修复大地水脉.可是六界之间皆有封 ...
- java应用高内存占用
在java虚拟机中,内存分为三个代:新生代(New), 老生代(Old).永久代(Perm) 新生代: 新建的对象都存放这里老生代:存放从新生代中迁移过来的生命周期较久的对象.新生代和老生代共同组成了 ...
- 【贪心】【堆】bzoj1029 [JSOI2007]建筑抢修
按完成时限排序,一个个修复.若当前建筑花费时间+之前花费的总时间不超过时限,则ans++:否则,从之前已修复的建筑中挑一个耗时最多的,与当前建筑比较,若当前建筑更优,则更新ans. #include& ...
- 【二维偏序】【树状数组】【权值分块】【分块】poj2352 Stars
经典问题:二维偏序.给定平面中的n个点,求每个点左下方的点的个数. 因为 所有点已经以y为第一关键字,x为第二关键字排好序,所以我们按读入顺序处理,仅仅需要计算x坐标小于<=某个点的点有多少个就 ...
- 小白的Python之路 day5 os,sys模块详解
os模块详解 1.作用: 提供对操作系统调用的接口 2.常用方法: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径os.chdir("dirname" ...
- lua的luasocket程序
-- load namespace local socket = require("socket") -- create a TCP socket and bind it to t ...