APIO-抢掠计划解题思路与参考代码
抢掠计划
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
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
参考代码1
1 #include<queue> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 9 10 struct node 11 { 12 int y,next; 13 }map[500010],map2[500010]; 14 15 16 int n,m,tot,st,p,tim,top,cnt,tot2; 17 int first[500010],money[500010],bar[500010],cost[500010],first2[500010]; 18 int dfn[500010],low[500010],stack[500010],belong[500010],v[500010]; 19 bool instack[500010],b[500010]; 20 queue<int>que; 21 22 23 void insert(int x,int y) 24 { 25 tot++; 26 map[tot].y=y; 27 map[tot].next=first[x]; 28 first[x]=tot; 29 } 30 31 32 void Insert(int x,int y) 33 { 34 tot2++; 35 map2[tot2].y=y; 36 map2[tot2].next=first2[x]; 37 first2[x]=tot2; 38 } 39 40 41 void init() 42 { 43 scanf("%d%d",&n,&m); 44 for (int i=1;i<=m;i++) 45 { 46 int x,y; 47 scanf("%d%d",&x,&y); 48 insert(x,y); 49 } 50 for (int i=1;i<=n;i++) scanf("%d",&money[i]); 51 scanf("%d%d",&st,&p); 52 for (int i=1;i<=p;i++) scanf("%d",&bar[i]); 53 } 54 55 56 void Tarjan(int i) 57 { 58 dfn[i]=low[i]=++tim; 59 stack[++top]=i; 60 instack[i]=true; 61 int j,t=first[i]; 62 while (t!=0) 63 { 64 j=map[t].y; 65 if (!dfn[j]) 66 { 67 Tarjan(j); 68 if (dfn[j]<dfn[i]) dfn[i]=dfn[j]; 69 }else 70 if (instack[j] && low[j]<dfn[i]) dfn[i]=low[j]; 71 t=map[t].next; 72 } 73 if (dfn[i]==low[i]) 74 { 75 cnt++; 76 do 77 { 78 j=stack[top--]; 79 instack[j]=false; 80 belong[j]=cnt; 81 } 82 while (j!=i); 83 } 84 } 85 86 87 void scc() 88 { 89 for (int i=1;i<=n;i++) cost[belong[i]]+=money[i]; 90 for (int i=1;i<=n;i++) 91 { 92 int t=first[i]; 93 while (t!=0) 94 { 95 int j=map[t].y; 96 if (belong[i]!=belong[j]) 97 { 98 Insert(belong[i],belong[j]); 99 } 100 t=map[t].next; 101 } 102 } 103 } 104 105 106 void spfa() 107 { 108 int head,t; 109 memset(v,0,sizeof(v)); 110 memset(b,false,sizeof(b)); 111 v[st]=cost[st],b[st]=true,que.push(st); 112 while (!que.empty()) 113 { 114 head=que.front(); 115 t=first2[head]; 116 while (t!=0) 117 { 118 if (v[head]+cost[map2[t].y]>v[map2[t].y]) 119 { 120 v[map2[t].y]=v[head]+cost[map2[t].y]; 121 if (!b[map2[t].y]) 122 { 123 que.push(map2[t].y); 124 b[map2[t].y]=true; 125 } 126 } 127 t=map2[t].next; 128 } 129 que.pop(); 130 b[head]=false; 131 } 132 } 133 134 135 void work() 136 { 137 for (int i=1;i<=n;i++) 138 if (!dfn[i]) Tarjan(i); 139 scc(); 140 st=belong[st]; 141 spfa(); 142 int ans=0; 143 for (int i=1;i<=p;i++) ans=max(ans,v[belong[bar[i]]]); 144 printf("%d\n",ans); 145 } 146 147 148 int main() 149 { 150 init(); 151 work(); 152 return 0; 153 }
解题思想:
在一个有向图中,有环的存在。每个点有一个价值,而其中的某些点也许是将要到达的目的地,给定起点,求在这些预定目的地中,哪个目的地才是最后获得价值最大的点,输出最大价值。
我们都能看出来,要用spfa来求单源最长路,但是有环的存在,所以就要强连通分量+缩点之后,再进行spfa,因为数据较大,递归Tarjan爆栈了,又因为不会写非递归不会写,所以就这么着吧...
参考代码2
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 6 #define INF 0x7FFFFFFF 7 #define gmax(a,b) ((a)>(b)?(a):(b)) 8 #define gmin(a,b) ((a)<(b)?(a):(b)) 9 #define fill(a,b) memset(a,b,sizeof(a)) 10 #define MAXV 600000 11 #define MAXE 600000 12 13 using namespace std; 14 15 struct Edge{ 16 int ver; 17 Edge *next; 18 void SetData(int V, Edge *N){ 19 ver = V; 20 next = N; 21 } 22 }; 23 24 Edge edgepool[MAXE << 1]; 25 int edger; 26 Edge *onet[MAXV]; 27 Edge *net[MAXV]; 28 int dis[MAXV]; 29 int Q[MAXV]; 30 bool inQ[MAXV]; 31 int w[MAXV]; 32 int maxv; 33 int n, m; 34 int sccer; 35 int scc[MAXV]; 36 int dfn[MAXV]; 37 int low[MAXV]; 38 int dfner; 39 int S[MAXV]; 40 bool inS[MAXV]; 41 bool g[MAXV]; 42 43 void SPFA(int src){ 44 for (int i=1; i<maxv+1; i++){ 45 dis[i] = 0; 46 } 47 fill(inQ, 0); 48 int head, tail; 49 head = tail = 0; 50 dis[src] = 0; 51 if (++tail == maxv) 52 tail = 0; 53 Q[tail] = src; 54 inQ[src] = 1; 55 while (head != tail){ 56 if (++head == maxv) 57 head = 0; 58 int u = Q[head]; 59 inQ[u] = 0; 60 for (Edge *e=net[u]; e; e=e->next){ 61 if (dis[e->ver] < dis[u] + w[e->ver]){ 62 dis[e->ver] = dis[u] + w[e->ver]; 63 if (!inQ[e->ver]){ 64 if (++tail == maxv) 65 tail = 0; 66 Q[tail] = e->ver; 67 inQ[e->ver] = 1; 68 } 69 } 70 } 71 } 72 } 73 74 void InsOEdge(int u, int v){ 75 edgepool[++edger].SetData(v, onet[u]); 76 onet[u] = &edgepool[edger]; 77 } 78 79 void InsEdge(int u, int v){ 80 edgepool[++edger].SetData(v, net[u]); 81 net[u] = &edgepool[edger]; 82 } 83 84 void DfsSCC(int u){ 85 dfn[u] = low[u] = ++dfner; 86 S[++S[0]] = u; 87 inS[u] = 1; 88 for (Edge *e=onet[u]; e; e=e->next){ 89 if (!dfn[e->ver]){ 90 DfsSCC(e->ver); 91 low[u] = gmin(low[u], low[e->ver]); 92 } 93 else 94 if (inS[e->ver]){ 95 low[u] = gmin(low[u], dfn[e->ver]); 96 } 97 } 98 // 99 if (low[u] == dfn[u]){ 100 sccer++; 101 while (1){ 102 int t = S[S[0]--]; 103 inS[t] = 0; 104 scc[t] = sccer; 105 if (t == u) 106 break; 107 } 108 } 109 } 110 111 int main(){ 112 113 freopen("atm.in", "r", stdin); 114 freopen("atm.out", "w", stdout); 115 116 int i; 117 scanf("%d %d", &n, &m); 118 while (m--){ 119 int u, v; 120 scanf("%d %d", &u, &v); 121 InsOEdge(u, v); 122 } 123 // 124 fill(dfn, 0); 125 fill(scc, 0); 126 dfner = sccer = 0; 127 for (i=1; i<n+1; i++){ 128 if (!dfn[i]) 129 DfsSCC(i); 130 } 131 for (i=1; i<n+1; i++){ 132 for (Edge *e=onet[i]; e; e=e->next){ 133 if (scc[i] != scc[e->ver]){ 134 InsEdge(scc[i], scc[e->ver]); 135 } 136 } 137 } 138 // 139 maxv = sccer; 140 for (i=1; i<n+1; i++){ 141 int t; 142 scanf("%d", &t); 143 w[scc[i]] += t; 144 } 145 // 146 int src; 147 scanf("%d", &src); 148 src = scc[src]; 149 SPFA(src); 150 // 151 scanf("%d", &m); 152 int ans = 0; 153 while (m--){ 154 int t; 155 scanf("%d", &t); 156 t = scc[t]; 157 ans = gmax(ans, dis[t]); 158 } 159 ans += w[src]; 160 printf("%d\n", ans); 161 162 // 163 fclose(stdin); 164 fclose(stdout); 165 166 return 0; 167 }
首先想到最短路,
然后自然就去处理强联通分量(SCC),
于是算法出来了:
强联通缩点之后单源最长路。
评测时发现:果然有2个点爆栈(Crash栈溢出)了。。好在Linux下堆栈限制会比较宽,但是不知道是不是Linux下能过。
据说-O2对堆栈有优化,但是没实践过(我也懒了。。)。
还有个-m32不知道是干什么用的。。
反正。。这样一个程序要是真的模拟堆栈的话。。我宁可少得20分去做别的题。~~
posted on 2012-07-15 17:36 AlanLau2011 阅读(716) 评论(0) 收藏 举报

浙公网安备 33010602011771号