NC23832 Magic Slab(最小割)

 

 假设所有奖励都能拿到的值sum(每个格子和另外奖励)

每个格子的建图不难想到如下——要么放弃cij的收益,要么消耗ai和bj的值

 关联奖励如何建图呢,我们将它作为点单独处理,然后向行列连四个边就行了

答案就是sum-最小割 ,这是网络流做法

我们再看下,花ai,bj去获得cij的收益,也就是-ai-bj -> cij,这是什么?求有向图最大权闭合子图

建图和这题网络流建图一样,cij左集合,ai,bj右集合,权边取绝对值

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pb push_back
#define IOS ios::sync_with_stdio(false),cin.tie(0)
//#define int long long
#define inf 0x3f3f3f3f
#define pii pair<int ,int >
#define fi first
#define se second
typedef long long ll;
const int N=3e3+10;
int n,np,nc,m;
int s,t;
int d[N];
int mp[50][50];
struct ty
{
    int t,next,flow;
}edge[2*10010];
int tot=1,head[N];
void add(int x ,int y, int flow)
{
    edge[++tot]={y,head[x],flow};
    head[x]=tot;
    edge[++tot]={x,head[y],0};
    head[y]=tot;
}
void ini()
{
    tot=1;
    mst(head,-1);
}
bool bfs(int t)
{
    mst(d,0);
    queue <int> q;
    q.push(s);
    d[s]=1;
    while( !q.empty() )
    {
        int x = q.front();
        q.pop();
        if( x==t ) return true;
        for(int i=head[x] ;i!=-1;i=edge[i].next)
        {
            int y = edge[i].t;
            if( edge[i].flow > 0 && !d[y] )
            {
                d[y] = d[x] + 1;
                q.push(y);
            }
        }
    }
    return false;
}
int dfs(int x ,int flow ,int t)
{
    if( x==t ) return flow;
    int sum=0;
    for(int i=head[x] ;i!=-1;i=edge[i].next)
    {
        int y = edge[i].t;
        if( d[y] == d[x] +1 && edge[i].flow>0 )
        {
            int temp = dfs(y,min(edge[i].flow , flow-sum),t);
            edge[i].flow -= temp;
            edge[i^1].flow += temp;
            sum += temp;
            if( sum == flow ) return flow;
        }
}
    if( !sum ) d[x]=0;
    return sum;
}
int get(int x ,int y)
{
    return n*2+(x-1)*n+y;
}
signed main()
{
//    freopen("data.txt","r",stdin);
    ini();
    cin>>n>>m;
    s=0,t=n*n+2*n+m+1;
    int sum=0;
    _for(i,1,n) _for(j,1,n)
    {
         cin>>mp[i][j],sum+=mp[i][j];
         add(s,get(i,j),mp[i][j]);
         add(get(i,j),i,inf);
         add(get(i,j),j+n,inf);
    }
    _for(i,1,n)
    {
        int x;cin>>x;
        add(i,t,x);
    }
    _for(i,1,n)
    {
        int x;cin>>x;
        add(i+n,t,x);
    }
    _for(i,1,m)
    {
        int a,b,c,d,e;cin>>a>>b>>c>>d>>e;
        sum += e;
        add(s,n*n+2*n+i,e);
        add(n*n+2*n+i,a,inf);
        add(n*n+2*n+i,b+n,inf);
        add(n*n+2*n+i,c,inf);
        add(n*n+2*n+i,d+n,inf);
    }

    //cij和ai,bj不能共存
    //行列和s连边
    int ans=0;
    while( bfs(t) ) ans+=dfs(s,inf,t);
    cout<<sum-ans<<endl;//减去最小割
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值