您的位置:首页 > 编程语言

APIO-抢掠计划解题思路与参考代码

2012-07-15 17:36 295 查看
抢掠计划
Siruseri城中的道路都是单向的。不同的道路由路口连接。按照法律的规定,
在每个路口都设立了一个 Siruseri 银行的 ATM
取款机。令人奇怪的是,Siruseri
的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。
Banditji 计划实施
Siruseri有史以来最惊天动地的 ATM 抢劫。他将从市中心
出发,沿着单向道路行驶,抢劫所有他途径的ATM
机,最终他将在一个酒吧庆
祝他的胜利。
使用高超的黑客技术,他获知了每个ATM
机中可以掠取的现金数额。他希
望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。
他可
以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机
里面就不会再有钱了。

例如,假设该城中有6个路口,道路的连接情况如下图所示:

市中心在路口
1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表
示。每个ATM机中可取的钱数标在了路口的上方。在这个例子中,Banditji
能抢
劫的现金总数为47,实施的抢劫路线是:1-2-4-1-2-3-5。
输入格式
第一行包含两个整数 N、M。N
表示路口的个数,M表示道路条数。接下来
M行,每行两个整数,这两个整数都在 1到 N 之间,第i+1 行的两个整数表示第
i
条道路的起点和终点的路口编号。接下来 N 行,每行一个整数,按顺序表示每
个路口处的ATM机中的钱数。接下来一行包含两个整数
S、P,S表示市中心的
编号,也就是出发的路口。P表示酒吧数目。接下来的一行中有 P个整数,表示
P个有酒吧的路口的编号。
输出格式

输出一个整数,表示 Banditji 从市中心开始到某个酒吧结束所能抢劫的最多
的现金总数。

数据范围
50%的输入保证
N, M<=3000。所有的输入保证 N, M<=500000。每个 ATM
机中可取的钱数为一个非负整数且不超过
4000。输入数据保证你可以从市中心
沿着Siruseri的单向的道路到达其中的至少一个酒吧。
输入样例
6 7
1 2

2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1

5
1 4
4 3 5 6
输出样例
47
参考代码2

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define INF 0x7FFFFFFF
#define gmax(a,b) ((a)>(b)?(a):(b))
#define gmin(a,b) ((a)<(b)?(a):(b))
#define fill(a,b) memset(a,b,sizeof(a))
#define MAXV 600000
#define MAXE 600000

using namespace std;

struct Edge{
int ver;
Edge *next;
void SetData(int V, Edge *N){
ver = V;
next = N;
}
};

Edge edgepool[MAXE << 1];
int edger;
Edge *onet[MAXV];
Edge *net[MAXV];
int dis[MAXV];
int Q[MAXV];
bool inQ[MAXV];
int w[MAXV];
int maxv;
int n, m;
int sccer;
int scc[MAXV];
int dfn[MAXV];
int low[MAXV];
int dfner;
int S[MAXV];
bool inS[MAXV];
bool g[MAXV];

void SPFA(int src){
for (int i=1; i<maxv+1; i++){
dis[i] = 0;
}
fill(inQ, 0);
int head, tail;
head = tail = 0;
dis[src] = 0;
if (++tail == maxv)
tail = 0;
Q[tail] = src;
inQ[src] = 1;
while (head != tail){
if (++head == maxv)
head = 0;
int u = Q[head];
inQ[u] = 0;
for (Edge *e=net[u]; e; e=e->next){
if (dis[e->ver] < dis[u] + w[e->ver]){
dis[e->ver] = dis[u] + w[e->ver];
if (!inQ[e->ver]){
if (++tail == maxv)
tail = 0;
Q[tail] = e->ver;
inQ[e->ver] = 1;
}
}
}
}
}

void InsOEdge(int u, int v){
edgepool[++edger].SetData(v, onet[u]);
onet[u] = &edgepool[edger];
}

void InsEdge(int u, int v){
edgepool[++edger].SetData(v, net[u]);
net[u] = &edgepool[edger];
}

void DfsSCC(int u){
dfn[u] = low[u] = ++dfner;
S[++S[0]] = u;
inS[u] = 1;
for (Edge *e=onet[u]; e; e=e->next){
if (!dfn[e->ver]){
DfsSCC(e->ver);
low[u] = gmin(low[u], low[e->ver]);
}
else
if (inS[e->ver]){
low[u] = gmin(low[u], dfn[e->ver]);
}
}
//
if (low[u] == dfn[u]){
sccer++;
while (1){
int t = S[S[0]--];
inS[t] = 0;
scc[t] = sccer;
if (t == u)
break;
}
}
}

int main(){

freopen("atm.in", "r", stdin);
freopen("atm.out", "w", stdout);

int i;
scanf("%d %d", &n, &m);
while (m--){
int u, v;
scanf("%d %d", &u, &v);
InsOEdge(u, v);
}
//
fill(dfn, 0);
fill(scc, 0);
dfner = sccer = 0;
for (i=1; i<n+1; i++){
if (!dfn[i])
DfsSCC(i);
}
for (i=1; i<n+1; i++){
for (Edge *e=onet[i]; e; e=e->next){
if (scc[i] != scc[e->ver]){
InsEdge(scc[i], scc[e->ver]);
}
}
}
//
maxv = sccer;
for (i=1; i<n+1; i++){
int t;
scanf("%d", &t);
w[scc[i]] += t;
}
//
int src;
scanf("%d", &src);
src = scc[src];
SPFA(src);
//
scanf("%d", &m);
int ans = 0;
while (m--){
int t;
scanf("%d", &t);
t = scc[t];
ans = gmax(ans, dis[t]);
}
ans += w[src];
printf("%d\n", ans);

//
fclose(stdin);
fclose(stdout);

return 0;
}


首先想到最短路,

然后自然就去处理强联通分量(SCC),

于是算法出来了:

强联通缩点之后单源最长路。

评测时发现:果然有2个点爆栈(Crash栈溢出)了。。好在Linux下堆栈限制会比较宽,但是不知道是不是Linux下能过。

据说-O2对堆栈有优化,但是没实践过(我也懒了。。)。

还有个-m32不知道是干什么用的。。

反正。。这样一个程序要是真的模拟堆栈的话。。我宁可少得20分去做别的题。~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: