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
参考代码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)    收藏  举报

导航