[BZOJ1565]植物大战僵尸 最大权闭合图

BZOJ1565
题目有点长qwq,读了很久才看懂。
这似乎是一道最大权闭合图的裸题,但是我们发现存在植物互相保护这种情况,即建出来的图不一定是DAG。那么我们可以拓扑排序找环,然后把环忽略。然后把不可攻击的点(即环上的点)不与虚拟源点和虚拟汇点连边,那么就不会影响答案了。
这题也更加加深了对该建模方式的理解,即如果需要获得正权的收益,且该点通过奇妙重重的方式连接到对面(负权点),那么一定要在汇点割去负权的损失才能获得正权的收益。
所以答案=正权和-最小割
(没想到瞎写一通还能1A
(逃

#include<bits/stdc++.h>
#define maxn 610
#define inf 0x3f3f3f3f
using namespace std;
struct node{
int to,f,m;
node*next,*rev; 
}*con[maxn];
void addedge(int x,int y,int m)
{
    node*p=new node;
    p->to=y;
    p->f=0;p->m=m;
    p->next=con[x];
    con[x]=p;
    p=new node;
    p->to=x;
    p->f=p->m=0;
    p->next=con[y];
    con[y]=p;
    con[x]->rev=con[y];
    con[y]->rev=con[x];
}
int n,m,s,t,pts[maxn],de[maxn],dep[maxn];
int poi(int x,int y) {return x*m+y+1;}
bool can[maxn];
int dl[maxn],head=1,tail=0,ans;
void toposort()
{
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
        if(!de[poi(i,j)]) dl[++tail]=poi(i,j),can[poi(i,j)]=true;
    while(head<=tail)
    {
        int v=dl[head++];
        for(node*p=con[v];p;p=p->next)
        if(p->m==0) {
            de[p->to]--;
            if(!de[p->to]) dl[++tail]=p->to,can[p->to]=true;
        }
    }
}
bool bfs()
{
    bool tmp=false;
    memset(dep,-1,sizeof(dep));
    head=tail=1;
    dl[tail]=s;dep[s]=1;
    while(head<=tail)
    {
        int v=dl[head++];
        if(v==t) tmp=true;
        for(node*p=con[v];p;p=p->next)
        if(p->f<p->m&&dep[p->to]==-1)
            dep[p->to]=dep[v]+1,dl[++tail]=p->to;
    }
    return tmp;
}
int dinic(int v,int e)
{
    int o=0,temp=0;
    if(dep[v]==-1) return 0;
    if(v==t) return e;
    for(node*p=con[v];p;p=p->next){
    if(dep[p->to]==dep[v]+1&&p->f<p->m)
    {
        o=dinic(p->to,min(e-temp,p->m-p->f));
        temp+=o;
        p->f+=o;
        p->rev->f-=o;   
    }
    if(temp==e) break;
    }
    if(temp==0) dep[v]=-1;
    return temp;
}
int main()
{
    scanf("%d%d",&n,&m);
    s=0;t=n*m+1;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
        {
            int num,x,y;
            scanf("%d%d",&pts[poi(i,j)],&num);
            for(int k=1;k<=num;++k)
                scanf("%d%d",&x,&y),addedge(poi(x,y),poi(i,j),inf),de[poi(x,y)]++;
            if(j!=m-1) addedge(poi(i,j),poi(i,j+1),inf),de[poi(i,j)]++;
        }
        toposort();
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
        if(can[poi(i,j)]) {
            if(pts[poi(i,j)]>0) addedge(s,poi(i,j),pts[poi(i,j)]),ans+=pts[poi(i,j)];
                else addedge(poi(i,j),t,-pts[poi(i,j)]);}
    while(bfs())
        ans-=dinic(s,inf);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值