1565: [NOI2009]植物大战僵尸 【最大权闭合子图(网络流二元关系)】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88563170

题意:

n*m的植物矩阵,每个植物有价值v,吃掉一颗植物之前必须先吃掉与它同一行右边的植物,同时植物间有保护关系(x,y),表示吃掉x之前必须先吃掉y。

题目分析:

其实这个题似乎就是 最大权闭合子图问题:
在这里插入图片描述
考虑二元关系模型,x选则y必须选,也就是说x选、y不选的代价为inf
那么ans = 正权点之和 - 不选的正权 - 选的|负权|
可以按如图方式建边:
在这里插入图片描述
(vx是正权,vy是负权)
x选(保留与S的正边)且 y不选(保留与T的负边),那么有inf的代价,可以看出两者必须割一个。
(以上为简易过程的口胡)
让我们在来仔细地看一看边是怎么建出来的:
在这里插入图片描述
在这里插入图片描述
好了那就是裸裸的二元关系建图了…吧?
然后一发不过样例
发现这样只能解决无环的图,如果限制条件形成了环,我们并没有排除掉整个环一起选的情况。
所以需要tarjan缩点,如果形成了点数大于1的强联通分量,就把这个分量选的代价置为inf,不同强连通分量之间的边照常连即可。

PS:一直打矩阵的题都要给(i,j)编个号,结果这道题刷新了我的认知。。可以用vector直接存下它的关系边,for循环直接从1到n*m就行了!!!清爽无比~

Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#include<algorithm>
#define maxn 605
#define maxm 400005
using namespace std;
inline void read(int &a){//一开始读入优化没有判负数。。
    char c;bool f=0;
    while(!isdigit(c=getchar())) if(c=='-') f=1;
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
    if(f) a=-a;
}
const int inf = 0x3f3f3f3f;
int n,m,S,T,sum;
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],c[maxm],tot=1;
inline void line(int x,int y,int z){
    nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
    nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
namespace Maxflow{
    int d[maxn],vd[maxn],sz;
    int aug(int u,int augco)
    {
        if(u==T) return augco;
        int need=augco,delta;
        for(int i=cur[u];i;i=nxt[i]) if(c[i]&&d[u]==d[to[i]]+1){
            delta=aug(to[i],min(c[i],need));
            c[i]-=delta,c[i^1]+=delta;cur[u]=i;
            if(!(need-=delta)||d[S]==sz) return augco-need;
        }
        cur[u]=fir[u];
        if(!(--vd[d[u]])) d[S]=sz;
        vd[++d[u]]++;
        return augco-need;
    }
    int SAP(){
        memset(d,0,sizeof d);
        memset(vd,0,sizeof vd);
        int flow=0;sz=T+1;
        while(d[S]<sz) flow+=aug(S,inf);
        return flow;
    }
}
vector<int>e[maxn];
int a[maxn],x,y,z;
int dfn[maxn],low[maxn],tim,scc[maxn],siz[maxn],stk[maxn],top,scnt;
void tarjan(int u){
    dfn[u]=low[u]=++tim;
    stk[++top]=u;
    for(int i=e[u].size()-1,v;i>=0;i--){
        if(!dfn[v=e[u][i]]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(!scc[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        ++scnt;
        do scc[stk[top]]=scnt,siz[scnt]++; while(stk[top--]!=u);
    }
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n*m;i++){
        read(a[i]);
        if(i%m) e[i].push_back(i+1);
        read(z);while(z--) read(x),read(y),e[x*m+y+1].push_back(i);
    }
    for(int i=1;i<=n*m;i++) if(!dfn[i]) tarjan(i);
    S=0,T=scnt+1;
    for(int i=1;i<=scnt;i++) if(siz[i]>1) line(i,T,inf);
    for(int i=1;i<=n*m;i++){
        x=scc[i];
        if(siz[x]==1){
            if(a[i]>0) sum+=a[i],line(S,x,a[i]);
            if(a[i]<0) line(x,T,-a[i]);
        }
        for(int j=e[i].size()-1;j>=0;j--) if(x!=(y=scc[e[i][j]])) line(x,y,inf);
    }
    printf("%d",sum-Maxflow::SAP());
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/88563170