[JZOJ3447]摘取作物(SPFA费用流模板)

Description

Feather的农场里有N*M块地,排列成N行,每行M块地。Feather在每块地里种植了不同的农作物。现在这些农作物都成熟了,可以摘取下来出售了。其中第i行第j列的地里的农作物的价值为W[i,j]。

JackRabbit是Feather的好友,平时经常为Feather的农作物除草除虫。为了答谢JackRabbit,Feather决定把一部分农作物送给JackRabbit。JackRabbit很高兴,恨不得一下子把农场里的农作物摘空。

为了防止JackRabbit把农作物摘空,Feather提出了两个条件:

1.每行最多选取两块地;

2.每列最多选取两块地。

这下子把JackRabbit难住了。如何在满足这两个条件的前提下,使得摘取的农作物的价值之和最大呢?

Solution

很明显的,每行建一个点,每列建一个点,连边随便YY一下,跑个最大费用可行流就好

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define N 35
using namespace std;
int n,m,a[2*N][2*N],f[2*N][2*N],w[2*N][2*N],v[N][N],n1,s,dt[10*N],dis[2*N][3];
bool bz[2*N];
int link(int x,int y,int r,int p)
{
    a[x][++a[x][0]]=y;
    a[y][++a[y][0]]=x;
    f[x][y]=r;
    w[x][y]=p;
    w[y][x]=-p;
}
bool spfa()
{
    memset(bz,0,sizeof(bz));
    memset(dis,128,sizeof(dis));
    dt[1]=1;
    bz[1]=1;
    dis[1][0]=0;
    int p,k,l=0,r=1,i;
    while(l<r)
    {
        k=dt[++l];
        fo(i,1,a[k][0])
        {
            p=a[k][i];
            if (dis[p][0]<dis[k][0]+w[k][p]&&f[k][p]>0)
            {
                dis[p][0]=dis[k][0]+w[k][p];
                dis[p][1]=k;
                if (bz[p]==0)
                {
                    dt[++r]=p;
                    bz[p]=1;
                }
            }
        }
        bz[k]=0;
    }
    return(dis[n1][0]>0);
}
void get()
{
    int i=n1,j=dis[i][1];
    s+=dis[n1][0];
    while (j>0)
    {
        f[j][i]-=1;
        f[i][j]+=1;
        i=j;
        j=dis[j][1];
    }
}
int main()
{
    cin>>n>>m;
    int i,j;
    fo(i,1,n)
        fo(j,1,m)
            scanf("%d",&v[i][j]);
    n1=n+m+2;
    fo(i,1,n) link(1,1+i,2,0);
    fo(i,1,m) link(1+n+i,n1,2,0);
    fo(i,1,n)
        fo(j,1,m) link(1+i,1+n+j,1,v[i][j]);
    while (spfa()) get();
    cout<<s;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值