bzoj 5403: marshland 费用流

Description
这里写图片描述
Input
这里写图片描述
Output
这里写图片描述
Sample Input

Input 1
3 3 1
0 1 0
2 0 1
0 1 0
1 3
Input 2
3 3 4
0 2 0
0 0 4
0 3 0
1 3
2 1
2 2
3 1

Sample Output

Output 1
3
Output 2
9

Data Constraint
这里写图片描述

分析:
今天模拟赛,出题人做了自己出的题目……
出题人表示看到题目非常尴尬,然后ak了。

x + y x+y x+y为奇数的为黑点,否则为白点。
显然我们选一个黑点,就要在上下两个中选一个白点和一个在左右两个中选一个白点。
那么就可以给费用流建模了。我们把横坐标为偶数的白点放在左边,横坐标为奇数的放右边。黑点分成两个点,这两个点连一条费用为 v ( x , y ) v_{(x,y)} v(x,y)的边,横坐标为偶数的白点点连左边的黑点,右边的黑点连横坐标为奇数的白点,然后连上源汇就好了。显然一次只会加 1 1 1点流量,所以当跑了 m m m次,或最长路为负即可退出(显然不一定要加上所有的点)。
考场上一直在想怎样一次流掉这两个点,一直把这个放在一边,然后就想不出来了。

代码:

/**************************************************************
    Problem: 5403
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:1264 ms
    Memory:3872 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
 
const int maxn=1e4+7;
const int maxe=1e5+7;
const int inf=0x3f3f3f3f;
 
using namespace std;
 
int n,m,k,x,ans,cnt,s,t,y;
int ls[maxn],dis[maxn],vis[maxn],pre[maxn],a[100][100],b[maxn];
 
queue <int> q;
 
struct edge{
    int x,y,w,c,op,next;
}g[maxe];
 
void add(int x,int y,int w,int c)
{
    g[++cnt]=(edge){x,y,w,c,cnt+1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){y,x,0,-c,cnt-1,ls[y]};
    ls[y]=cnt;
}
 
int po(int x,int y)
{
    return (x-1)*n+y;
}
 
bool spfa()
{   
    while (!q.empty()) q.pop();
    for (int i=s;i<=t;i++)
    {
        dis[i]=-inf;
        vis[i]=0;
    }
    dis[s]=0;
    q.push(s);
    vis[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[y]<dis[x]+g[i].c))
            {
                dis[y]=dis[x]+g[i].c;
                pre[y]=i;
                if (!vis[y])
                {
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
        vis[x]=0;
    }
    if (dis[t]>0) return 1;
    return 0;
}
 
void mcf()
{
    int i=t,x=1e9;
    while (i!=s)
    {
        x=min(x,g[pre[i]].w);
        i=g[pre[i]].x;
    }
    i=t;
    while (i!=s)
    {
        g[pre[i]].w-=x;
        g[g[pre[i]].op].w+=x;
        ans-=g[pre[i]].c*x;
        i=g[pre[i]].x;
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    s=0; t=n*n*2+1;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    for (int i=1;i<=k;i++)
    {
        scanf("%d%d",&x,&y);
        b[po(x,y)]=1;
    }   
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            ans+=a[i][j];
            if (b[po(i,j)]) continue;
            if ((i+j)%2==0)
            {
                if (i%2==0)
                {
                    add(s,po(i,j),1,0);
                    if (i>1) add(po(i,j),po(i-1,j),1,0);
                    if (j>1) add(po(i,j),po(i,j-1),1,0);
                    if (i<n) add(po(i,j),po(i+1,j),1,0);
                    if (j<n) add(po(i,j),po(i,j+1),1,0);
                }
                else
                {
                    add(po(i,j),t,1,0);                 
                    if (i>1) add(n*n+po(i-1,j),po(i,j),1,0);
                    if (j>1) add(n*n+po(i,j-1),po(i,j),1,0);
                    if (i<n) add(n*n+po(i+1,j),po(i,j),1,0);
                    if (j<n) add(n*n+po(i,j+1),po(i,j),1,0);
                }
            }
            else
            {
                add(po(i,j),n*n+po(i,j),1,a[i][j]);
            }
        }
    }
    for (int i=1;i<=m;i++)
    {
        if (spfa()) mcf();
    }
    printf("%d",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值