bzoj 1179 Atm

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1179

题解:

  一道比较综合的图论题

  直接讲正解:
  如果这个图G中存在某个强连通分量,那么这个强连通分量中的所有ATM即可视为都被抢到,所有的酒吧都可视为重点,并且也可以从这个强连通分量的任何结点出发继续向外扩展

  所以先做一遍Tarjan,找出强连通分量,然后重新构图,把每个强连通分量缩成一个点,此点的权值即为原先强连通分量里所有点权之和,判断此点中有没有酒吧,再将原先所有连接强连通分量的边连接在这个点

  最后做SPFA,找出权值最大的路径

  这道题时间限制15s,空间162MB,所以一般不会TLE或MLE

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 #define MAXN 500010
  6 #define MAXM 500010
  7 int n,m,time,cnt,head1[MAXN],DFN[MAXN],Low[MAXN],stack[MAXN],top,a1[MAXN],belong[MAXN];
  8 bool vis[MAXN],bar1[MAXN];
  9 struct node1
 10 {
 11     int v,next;
 12 }edge1[MAXM];
 13 //以上变量基本是在进行tarjan(及之前)使用
 14 int heads[MAXN],mm,a2[MAXN],s,p,d[MAXN],q[MAXN],head,tail,ans;
 15 bool viss[MAXN];
 16 struct node2
 17 {
 18     int v,next;
 19 }edge2[MAXM];
 20 //以上变量是在进行SPFA时使用
 21 //a存放点权,bar记录是否有酒吧
 22 bool bar2[MAXN];
 23 void add(int x,int y)
 24 {
 25     edge1[++cnt].next=head1[x];
 26     head1[x]=cnt;
 27     edge1[cnt].v=y;
 28 }
 29 void buildmap(int k)//重新建图、连边
 30 {
 31     for(int i=1;i<=n;i++)
 32     {
 33         if(belong[i]==k)
 34         {
 35             for(int j=head1[i];j;j=edge1[j].next)
 36             {
 37                 if(belong[edge1[j].v]==k)continue;
 38                 edge2[++m].next=heads[k];
 39                 heads[k]=m;
 40                 edge2[m].v=belong[edge1[j].v];
 41             }
 42         }
 43     }
 44 }
 45 void tarjan(int u)
 46 {
 47     DFN[u]=Low[u]=++time;
 48     vis[u]=true;
 49     stack[++top]=u;
 50     for(int i=head1[u];i;i=edge1[i].next)
 51     {
 52         int v=edge1[i].v;
 53         if(!DFN[v])
 54         {
 55             tarjan(v);
 56             Low[u]=min(Low[u],Low[v]);
 57         }
 58         else if(vis[v])Low[u]=min(Low[u],DFN[v]);
 59     }
 60     if(DFN[u]==Low[u])
 61     {
 62         int i;
 63         cnt++;
 64         do
 65         {
 66             i=stack[top--];
 67             vis[i]=false;
 68             belong[i]=cnt;
 69             a2[cnt]+=a1[i];
 70             if(bar1[i])bar2[cnt]=true;//先更新点权和是否有酒吧
 71         }while(u!=i);
 72     }
 73 }
 74 void SPFA()
 75 {
 76     head=1;tail=2;
 77     q[1]=s;
 78     viss[s]=1;
 79     while(head<tail)
 80     {
 81         for(int i=heads[q[head]];i!=0;i=edge2[i].next)
 82         {
 83             if(d[q[head]]+a2[edge2[i].v]>d[edge2[i].v])//注意,这里不是边权,是点权
 84             {
 85                 d[edge2[i].v]=d[q[head]]+a2[edge2[i].v];
 86                 if(!viss[edge2[i].v])
 87                 {
 88                     q[tail++]=edge2[i].v;
 89                     viss[edge2[i].v]=true;
 90                 }
 91             }
 92         }
 93         viss[q[head]]=false;
 94         head++;
 95     }
 96 }
 97 int main()
 98 {
 99     scanf("%d%d",&n,&m);
100     for(int i=1;i<=m;i++)
101     {
102         int x,y;
103         scanf("%d%d",&x,&y);
104         add(x,y);
105     }
106     for(int i=1;i<=n;i++)
107     {
108         scanf("%d",&a1[i]);
109     }
110     scanf("%d%d",&s,&p);
111     for(int i=1;i<=p;i++)
112     {
113         int x;
114         scanf("%d",&x);
115         bar1[x]=true;
116     }
117     cnt=0;
118     m=0;
119     for(int i=1;i<=n;i++)
120         if(!DFN[i])tarjan(i);
121     for(int i=1;i<=cnt;i++)
122         buildmap(i);
123     s=belong[s];
124     for(int i=1;i<=cnt;i++)
125     {
126         d[i]=a2[i];
127     }
128     SPFA();
129     for(int i=1;i<=cnt;i++)
130     {
131         if(bar2[i])ans=max(ans,d[i]);
132     }
133     printf("%d",ans);
134     return 0;
135 }
时间: 2024-12-15 07:10:42

bzoj 1179 Atm的相关文章

BZOJ 1179 Atm 题解

BZOJ 1179 Atm 题解 SPFA Algorithm Tarjan Algorithm Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,

BZOJ 1179 Atm(强连通分量缩点+DP)

题目说可以通过一条边多次,且点权是非负的,所以如果走到图中的一个强连通分量,那么一定可以拿完这个强连通分量上的money. 所以缩点已经很明显了.缩完点之后图就是一个DAG,对于DAG可以用DP来求出到达每一个点的money最大值.具体实现我用的是bfs. 然后如果一个强连通分量内有酒馆,那么这个点就可以更新答案啦. # include <cstdio> # include <cstring> # include <cstdlib> # include <iost

BZOJ 1179: [Apio2009]Atm( tarjan + 最短路 )

对于一个强连通分量, 一定是整个走或者不走, 所以tarjan缩点然后跑dijkstra. --------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define foreach(i,

[BZOJ 1179][APIO 2009]Atm

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1179 个人感觉此题比省选题简单多了,大概是POJ中档题的难度... 首先我们把这个有向图缩个点,缩点后的图是个DAG,新图中每个点的权值是对应强连通分量中的点的权值之和,新图中每个点对应的强连通分量中的点都是相互可达的,也就是说新图中的每个点,劫匪都能一次性抢完其中所有ATM机的钱. 然后对这个新图跑SPFA,SPFA本来是搞边权的,在这个题里,把SPFA稍微改改就能跑点权了,类

bzoj 1179: [Apio2009]Atm

Description Input 第 一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路 的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就 是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的

bzoj 1179[Apio2009]Atm (tarjan+spfa)

题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 输出 输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数. 样例输入 6 7 1

BZOJ 1179 APIO 2009 Atm Tarjan+SPFA

题目大意:给出一张有向图,每一个节点有一个权值,经过一次之后会取走节点上的权值.有一个原点,多个汇点,问最多能收获多少权值. 思路:做一次Tarjan将图变成拓扑图,然后直接跑SPFA+Heap,比较慢,但是用了高大上的namespace,很开心. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm>

【BZOJ 1179】[Apio2009]Atm

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] tarjan强连通缩点一下. 然后把缩点之后,每个点的钱的数累加起来. 然后从S出发 开始一边做bfs一遍做dp. 最后输出有酒吧的点的dp值中的最大值. [代码] /* n个点,m条有向边. 把有向图G的环进行缩点; 缩完之后的图存在vector <int> g[N]里面; n变为缩完点之后的图的节点的个数了. */ #include <bits/stdc++.h> using namespace std; #d