(纪中)3447. 摘取作物 (pick)【费用流】

67 篇文章 0 订阅
3 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述


解题思路

新算法之:最小费用最大流问题

又是一个我不会的算法啊啊啊啊啊 ,因为不知道如何讲解,且这篇博客十分详细,就不加描述了。

看完算法之后,回来这道题,先考虑构图:

对于每一行,用一个点表示,因为每行最多选两块地,所以从源点 s s s向这 n n n个行点连一条容量为 2 2 2,费用为 0 0 0的边。
对于每一列,同样用一个点表示,因为每列最多选两块地,所以从这 m m m个列点向汇点 t t t连一条容量为 2 2 2,费用为 0 0 0的边。
最后,因为在同一行、同一列最多只有一块的,所以,从 n n n个行点向 m m m个列点连一条容量为 1 1 1,费用为 W [ i , j ] W[i,j] W[i,j]的边。

在这里插入图片描述


代码

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;

int n,m;
int ans,f[100][100],c[100][100],dis[100100],next[100100],v[100100],q[100100];//f[i][j]表示点i,j间的流量,c[i][j]表示i,j间的花费

bool spfa(){
	 memset(next,0,sizeof(next));
    memset(v,0,sizeof(v));
	int h=0,t=1;
	for(int i=0;i<=n+m+1;i++)
		dis[i]=-2147483600;
	q[1]=0,dis[0]=0;
	while(h<t)
	{
		int x=q[++h];
		v[x]=0;
		for(int i=0;i<=n+m+1;i++)
		{
			if(f[x][i]&&dis[i]<dis[x]+c[x][i])//求最大花费:松弛
			{
				dis[i]=dis[x]+c[x][i];
				next[i]=x;
				if(!v[i])
				{
					q[++t]=i;
					v[i]=1;
				}
			}
		}
	}
	if(dis[n+m+1]<=0)return 0;//判断是否成为一条可行流
	return 1;
}

void work(){
	int sum=2147483600;
	for(int i=n+m+1;i;i=next[i])
		sum=min(sum,f[next[i]][i]);//求最大流量
	for(int i=n+m+1;i;i=next[i])
	{
		f[next[i]][i]-=sum;
		f[i][next[i]]+=sum;//更新反边,让其可以反悔
		ans+=c[next[i]][i]*sum;//统计花费
	}
}

int main(){
	freopen("pick.in","r",stdin);
    freopen("pick.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    	f[0][i]=2;
    for(int i=1;i<=m;i++)
		f[n+i][n+m+1]=2;	 
    for(int i=1;i<=n;i++)
    {
    	for(int j=1;j<=m;j++)
    	{
    		scanf("%d",&c[i][n+j]);
    		c[n+j][i]=-c[i][n+j];
    		f[i][n+j]=1;
		}
	}//建图
	while(spfa())work();
	printf("%d",ans); 
}
/*
8 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8

*/ 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值