LightOJ - 1071-Baker Vai (最小费用流)

原题地址:点击打开链接

题意:给你一个矩阵,每个点上都有一个数字代表需要被救的人数,你从(1,1)点出发开始救人走过的点不能再走,不过你只能向右或者向下走,到达终点后(m,n)返回起点,只能向上或者左走,每路过一个点就可以救该点有的人数,问你最多可以就多少人。

思路:可转化为最小费用流问题,关键的难点在于建图,该题需要拆点,每个点到自身的流量为1,(起点和终点的流量为2),然后根据顶点之间的可达性进行建边,最后注意结果要将起点和终点的人数减去一次,因为多加了一次。

#include<stdio.h>
#include<string.h>
#include<vector>
#define INF 1<<31-1
#define min(x,y)(x<y?x:y)
using namespace std;
struct Edge
{
	int to;
	int cap;
	int rev;
	int cost;
};
vector<Edge>g[20010]; 
void add_edge(int from,int to,int cap,int cost)
{
	g[from].push_back((Edge){to,cap,g[to].size(),cost});
	g[to].push_back((Edge){from,0,g[from].size()-1,-cost});
}
int id[110][110];
int prev[20010];
int pree[20010];
int dis[20010];
int min_cost_flow(int s,int t,int f)
{
	int res=0,i,u,v;
	bool update;
	while(f>0)
	{
		update=true;
		for(i=s;i<=t;i++)
		dis[i]=-INF;
		dis[s]=0;
		while(update)
		{
			update=false;
			for(u=1;u<=t;u++)
			{
				if(dis[u]==-INF)
				continue;
				for(i=0;i<g[u].size();i++)
				{
					Edge e=g[u][i];
					v=e.to;
					if(e.cap>0&&dis[v]<dis[u]+e.cost)
					{
						prev[v]=u;
						pree[v]=i;
						dis[v]=dis[u]+e.cost;
						update=true;
					}
				}
			}
		}
		if(dis[t]==-INF)
			break;
		for(u=t;u!=s;u=prev[u])
		{
			Edge &e=g[prev[u]][pree[u]];
			e.cap-=1;
			g[e.to][e.rev].cap+=1;
		}
		res+=dis[t];
		f-=1;
	}
	return res;
}
int main()
{
	int i,j,c,m,n,V,cost,num,u,v,s,t,k=0,x;
	scanf("%d",&c);
	while(c--)
	{
		num=0;
		x=0;
		scanf("%d%d",&m,&n);
		V=m*n;
		s=1;
		t=V*2;
		for(i=s;i<=t;i++)
		g[i].clear();
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++)
			{
				id[i][j]=++num;
				scanf("%d",&cost);
				if(num==1)
				{
					add_edge(num,num+V,2,cost);
					x+=cost;
				}
				else if(num==V)
					{
						x+=cost;
						add_edge(num,num+V,2,cost);
					}
				else
					add_edge(num,num+V,1,cost);
			}
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++)
			{
				int a=i+1;
				int b=j;
				if(a>=1&&a<=m&&b>=1&&b<=n)
				{
					u=id[i][j];
					v=id[a][b];
					add_edge(u+V,v,1,0);
				}
				a=i;
				b=j+1;
				if(a>=1&&a<=m&&b>=1&&b<=n)
				{
					u=id[i][j];
					v=id[a][b];
					add_edge(u+V,v,1,0);
				}
			}
			int res=min_cost_flow(s,t,2);
			printf("Case %d: %d\n",++k,res-x);
	}
	return 0;
} 
/*
2
3 3
1 1 1
1 0 1
1 1 1
3 4
1 1 0 1
1 1 1 1
0 1 10 1
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值