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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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

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

Sample Input

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

Sample Output

47

HINT

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

 

观察题目即可发现:1->2->4是一个强连通分量,3,5,6是一个强连通分量。很明显这是一道强连通分量的裸题。使用tarjan或各种神奇的求强连通分量的算法所完点后,剩下的就可以用各种算法来乱搞了。

先是tarjan求强连通分量部分

int stack[maxn],dfn[maxn],low[maxn],vis[maxn],belong[maxn];
int VAL[maxn],val[maxn];
int dfs_num,tot,top;
void tarjan(int curr){
    stack[++top]=curr;
    vis[curr]=1;
    dfn[curr]=low[curr]=++dfs_num;
    for(int i=first[curr];i;i=edge[i].next){
        int to = edge[i].to;
        if(!dfn[to]){
            tarjan(to);
            low[curr]=min(low[curr],low[to]);
        }
        else if(vis[to])
            low[curr]=min(low[curr],dfn[to]);
        
    }
    if(low[curr]==dfn[curr]){
        vis[curr]=0;
        belong[curr]=++tot;
        VAL[belong[curr]]+=val[curr];
        while(stack[top]!=curr){
            belong[stack[top]]=tot;
            VAL[belong[stack[top]]]+=val[stack[top]];
            vis[stack[top]]=0;
            top--;
        }
        top--;
    }
};

 

求完强连通分量,需要缩点建新图。注意:加边的时候变量名别搞混了。

void readd(){
    for(int i=1;i<=n;i++)
        for(int p=first[i];p;p=edge[p].next){
            if(belong[i]!=belong[edge[p].to]){
                G[++Gnt].from=belong[i];
                G[Gnt].to=belong[edge[p].to];
                G[Gnt].next=first2[belong[i]];
                 first2[belong[i]]=Gnt;                
            }
        }
}

接下来读题,既然要使得抢的钱最多,那么我们可以把它转化成一个最大路问题,用spfa求出源点到各点的最大距离,再判断各点是否是酒吧(符合要求的节点)。

void spfa(int s){
    queue <int>q ;
    dist[s]=VAL[s];//起点的钱也要抢走,所以初始值为起点所在SCC内所有的钱(dist数组初值默认为0)
    v[s]=1;
    q.push(s);
    while(!q.empty()){
        int from=q.front();q.pop();
        for(int i=first2[from];i;i=G[i].next){
            if(dist[from]+VAL[G[i].to]>dist[G[i].to]){ //这里是大于号
                dist[G[i].to]=dist[from]+VAL[G[i].to];
                if(!v[G[i].to]){
                    v[G[i].to]=1;
                    q.push(G[i].to);
                }
            }
        }
        v[from]=0;
    }
}

以下是完整AC代码

技术分享
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=500010;
int n,m;
int read(int &x){
    char c=getchar(),last;x=0;
    while(c<0||c>9)last=c,c=getchar();
    while(c<=9&&c>=0)x=(x<<1)+(x<<3)+c-0,c=getchar();
    if(last==-)x=-x;    
    return x;
}
int first[maxn],first2[maxn],cnt,Gnt;
struct EDGE{
    int from,to,next;
}edge[maxn],G[maxn];
void addedge(int u,int v){
    edge[++cnt].next=first[u];
    first[u]=cnt;
    edge[cnt].from=u;
    edge[cnt].to=v;
}

int stack[maxn],dfn[maxn],low[maxn],vis[maxn],belong[maxn];
int VAL[maxn],val[maxn];
int dfs_num,tot,top;
void tarjan(int curr){
    vis[stack[++top]=curr]=1;
    dfn[curr]=low[curr]=++dfs_num;
    for(int i=first[curr];i;i=edge[i].next){
        if(!dfn[edge[i].to]){
            tarjan(edge[i].to);
            low[curr]=min(low[curr],low[edge[i].to]);
        }
        else if(vis[edge[i].to]){
            low[curr]=min(low[curr],dfn[edge[i].to]);
        }
    }
    if(low[curr]==dfn[curr]){
        vis[curr]=0;
        VAL[belong[curr]=++tot]+=val[curr];
        while(stack[top]!=curr){
            VAL[belong[stack[top]]=tot]+=val[stack[top]];
            vis[stack[top--]]=0;
        }
        top--;
    }
};
int v[maxn],dist[maxn];
void readd(){
    for(int i=1;i<=n;i++)
        for(int p=first[i];p;p=edge[p].next){
            if(belong[i]!=belong[edge[p].to]){
                G[++Gnt].from=belong[i];
                G[Gnt].to=belong[edge[p].to];
                G[Gnt].next=first2[belong[i]];
                 first2[belong[i]]=Gnt;                
            }
        }
}
void spfa(int s){
    queue <int>q ;
    dist[s]=VAL[s];
    v[s]=1;
    q.push(s);
    while(!q.empty()){
        int from=q.front();q.pop();
        for(int i=first2[from];i;i=G[i].next){
            if(dist[from]+VAL[G[i].to]>dist[G[i].to]){
                dist[G[i].to]=dist[from]+VAL[G[i].to];
                if(!v[G[i].to]){
                    v[G[i].to]=1;
                    q.push(G[i].to);
                }
            }
        }
        v[from]=0;
    }
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        addedge(x,y);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    int s,p;
    scanf("%d %d",&s,&p);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(!belong[i])tarjan(i);
    }
    readd();
    spfa(belong[s]);
    for(int i=1;i<=p;i++){
        int x;
        scanf("%d",&x);
        ans=max(ans,dist[belong[x]]);
    }
    printf("%d",ans);
    return 0;
}
View Code

顺便给大家提供一组比较典型的数据点,当初提交迷之WA,跟别人的程序对拍了好久才找出来的。

13 16 
13 11 
12 5 
1 3 
10 9 
3 2 
2 10 
4 2 
3 12 
13 7 
13 1 
7 11 
5 10 
11 9 
3 1 
9 12 
9 3 
10 
3 
7 
1 
4 
9 
6 
8 
4 
5 
2 
11 
2 
13 5 
7 
10 
6 
9
1
Ouput: 54

 

以上是关于[BZOJ 1179]ATM题解 Tarjan缩点+SPFA的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1179: [Apio2009]Atm

BZOJ 1179[Apio2009]Atm

BZOJ1179Atm

bzoj1179: [Apio2009]Atm

bzoj1179[Apio2009]Atm

bzoj 1179 Atm