博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
poj 3592 Instantaneous Transference 【SCC +缩点 + SPFA】
阅读量:6676 次
发布时间:2019-06-25

本文共 9906 字,大约阅读时间需要 33 分钟。

Instantaneous Transference
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 NM (2 ≤ NM ≤ 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,- 1).

Output

For each test case output the maximum units of ores you can take.  

Sample Input

12 2111*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
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
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 
#include
#include
#include
#include
#include
#define MAXN 2000+10#define MAXM 3000000+10#define INF 0x3f3f3fusing 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
S;vector
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; }

转载于:https://www.cnblogs.com/gavanwanggw/p/7273367.html

你可能感兴趣的文章
开源新项目GitTest.com,欢迎大牛,小牛,菜鸟,同学加入发PR
查看>>
天啊,竟然忘了买春节后回上海的票。。。只能买区间票,中途再补票了。。。...
查看>>
内存数据网格hazelcast的一些机制原理
查看>>
Intellij idea下spark开发HelloWorld
查看>>
Gartner称:SaaS合同歧义引发安全隐忧
查看>>
[WCF安全系列]谈谈WCF的客户端认证[X.509证书认证]
查看>>
浅析:协同软件已成为用户应用软件采购最大热点?
查看>>
阿里云全新一代企业级新品解读—通过MaxCompute Studio实践大数据时代的DevOps
查看>>
中国人工智能学会通讯——个性化推荐和资源分配在金融和经济中的应用 1.4 智能金融·产品增强...
查看>>
云计算在安防监控领域中有哪些作用
查看>>
Python赶超R语言,成为数据科学、机器学习平台中最热门的语言?
查看>>
内部云部署不只虚拟化那么简单
查看>>
数据中心网络的那些二层技术谈
查看>>
Synergy配置与使用
查看>>
微服务的4大设计原则和19个解决方案
查看>>
一个跨平台的 C++ 内存泄漏检测器
查看>>
格力给洛阳砸了150亿,我们瞄到了更深刻的「原因」
查看>>
数据库事务隔离级别
查看>>
如何架设Linux打印服务器
查看>>
嫁接金融业 智能洞察是核心竞争力
查看>>