天天看點

網絡流 - 最大權閉合子圖 [NOI2009]植物大戰僵屍

算法思想概述

本題是一道最大權閉合子圖模型,應用的算法為最大流(BFS增廣即可),定理為最大流最小割定理,輔助算法為拓撲排序。

問題初始模組化

首先我們我建立圖論模型,把每個植物當做一個頂點,植物攜帶的資源數目為頂點的權值。如果一個植物b在另一個植物a的攻擊範圍内,連接配接一條有向邊<a,b>,表示a可以保護b。由于僵屍從右向左進攻,可以認為每個植物都被它右邊相鄰的植物保護,對于每個植物a(除最左邊一列),向其左邊的相鄰植物b,連接配接一條有向邊<a,b>。

由本題樣例就可以發現,有一些植物是互相依賴的,于是我們可以進行算法實作的第一步:

1.使用拓撲排序去除圖中的環,進而使圖得到簡化。

最大權閉合子圖

進行算法的第二步。

### 2.對第一步中得到的圖進行轉置操作(把所有邊反向),進而得到最大子權閉合圖。

樣例的圖經過拓撲排序和轉置操作後如下:

網絡流 - 最大權閉合子圖 [NOI2009]植物大戰僵屍

其中最大權閉合子圖為(1,2,4)

下面進行算法實作的第3、4步:

3.最大權閉合子圖的網絡流模組化:

(1). 建立附加源S和附加彙T。

(2). 圖中原有的轉置後的邊容量設為∞。

(3). 從S向每個權值為正的點連接配接一條容量為該點權值的有向邊。

(4). 從每個權值不為正的點向T連接配接一條容量為該點權值絕對值的有向邊。

模組化後圖如下:

網絡流 - 最大權閉合子圖 [NOI2009]植物大戰僵屍

4.求解:求S到T的最大流Maxflow,最大權閉合子圖的權值就是(所有正權點權值之和 – Maxflow),也就是需要輸出的答案。

我們可以這樣想,當我們從s到t的一條路上 得到了Ws 花費了min(Ws,Wt),如果Wt>Ws 得到收益為 Ws - Ws = 0(相當于不走);如果Wt<Ws,收益為Ws - Wt(賺錢了!!!);

而所有正權點權值之和就是

$\begin{matrix}\underbrace{Ws1+Ws2+\cdots+Wsn}\n\end{matrix}=\sum\limits_{i=1}^nW_i$

而S到T的最大流Maxflow就是

$\begin{matrix}\underbrace{min(Wt1,Ws1)+min(Wt2,Ws2)+\cdots+min(Wtn,Wsn)}\n\end{matrix}=\sum\limits_{i=1}^nmin(Wti,Wsi)$

$ $

$ ans = \sum\limits_{i=1}^nW_i-\sum\limits_{i=1}^nmin(Wti,Wsi) $

代碼

#include<bits/stdc++.h>
#define POINT(X, Y)  ((X) * 31 + (Y))
using namespace std;
const int MAXN = POINT(30,30)+10;
const int INF = 1<<28;

int read(){
    int flag=1,sum=0;char c;
    for(;c>'9'||c<'0';c=getchar())if(c=='-')flag=-1;
    for(;c<='9'&&c>='0';c=getchar())sum=(sum<<3)+(sum<<1)+c-'0';
    return flag*sum;
}

struct node{
    int to, val;
    int next=-1;
}edge[MAXN*MAXN*2];int top=1, head[MAXN];
vector<int> out[MAXN];int vis[MAXN],in[MAXN],score[MAXN];
int dep[MAXN],s=MAXN-1,t=MAXN-2;int n,m;

void add(int u, int v, int val) {
    top++;edge[top].to = v;edge[top].val = val;edge[top].next = head[u];head[u] = top;
    top++;edge[top].to = u;edge[top].val = 0;edge[top].next = head[v];head[v] = top;
}                

dinic部分

int bfs()
{
    memset(dep,0,sizeof(dep));
    queue<int> q;
    while(!q.empty()) 
        q.pop();
    q.push(s);
    dep[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(dep[v]==0&&edge[i].val>0)
            {
                dep[v]=dep[u]+1;
                q.push(v);
                if(v==t)return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow)
{
    if(u==t)
        return flow;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(dep[v]==dep[u]+1&&edge[i].val>0)
        {
            int k=dfs(v,min(edge[i].val,flow));
            if(k==0)
            {
                dep[v]=0;
            } 
            else
            {
                edge[i].val-=k;
                edge[i^1].val+=k;
                return k;       
            }
        }
    }
    return 0;
}
int dinic(){
    int flow,maxflow=0;
    while(bfs())
    {
        while(flow=dfs(s,INF))
        {
            maxflow+=flow;
        }
    }
    return maxflow;
}                

構造圖部分

void topsort(){
    queue<int> q;
    //memset(vis,0,sizeof(0));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(in[POINT(i,j)]==0){
                q.push(POINT(i,j));
                vis[POINT(i,j)]=1;
            }
        }
    while(!q.empty()){
        int u=q.front();q.pop();
        int len=out[u].size();
        for(int i=0;i<len;i++){
            int v=out[u][i];
            in[v]--;
            if(vis[v]==0&&in[v]==0){
                q.push(v);
                vis[v]=1;
            }
        }    
    }
}
int main(){
//  freopen("B.in","r",stdin);
//  freopen("B.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++){
        int flag,x,y;
        for(int j=1;j<=m;j++){
            score[POINT(i,j)]=read();
            flag=read();
            while(flag--){
                x=read();y=read();x++,y++;
                out[POINT(i,j)].push_back(POINT(x,y));
                in[POINT(x,y)]++;
            }
            if(j < m) {
                out[POINT(i, j + 1)].push_back(POINT(i, j));
                in[POINT(i, j)] ++;
            }
        }
    }
    topsort();
    int sum=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(in[POINT(i,j)]==0){
                int u = POINT(i, j);
                if(!vis[u])
                    continue;
                if(score[u] >= 0) {
                    add(s,u,score[u]);
                    sum += score[u];//cout<<sum<<" ";
                } else {
                    add(u,t,-score[u]);
                }
                for(int k = 0; k < out[u].size(); k ++) {
                    int v = out[u][k];
                    if(vis[v]) {
                        add(v, u, INF);
                    }
                }
            }
        }
    }
    cout<<sum-dinic();
    return 0;
}                

轉載于:https://www.cnblogs.com/starconstant/p/11154014.html