BZOJ 2502: 清理雪道(上下界最小流/上下界最小费用流)

34 篇文章 1 订阅
23 篇文章 0 订阅

滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。

这个题有两种思路,1是求带下界的最小流,2是求带下界的最小费用流。。。。。。
好板。
AC Code:(思路二)

#include<bits/stdc++.h>
#define maxn 205
#define maxm maxn * maxn
#define inf 0x3f3f3f3f
using namespace std;

int n,S,T,SS,TT,pcap[maxn];
int buf[maxn],info[maxn],Prev[maxm],to[maxm],cap[maxm],cst[maxm],cnt_e=1;
void Node(int u,int v,int c,int ct){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=ct,cap[cnt_e]=c; }
void Line(int u,int v,int c,int ct){ Node(u,v,c,ct),Node(v,u,0,-ct); }

int dis[maxn];
bool vis[maxn];
int cost = 0;
int aug(int now,int Max,int S,int T)
{
	if(now == T){ cost += dis[S] * Max; return Max; }
	int inc , st = Max;
	vis[now] = 1;
	for(int &i=info[now];i;i=Prev[i])
	{
		if(cap[i] && dis[to[i]] + cst[i] == dis[now] && !vis[to[i]])
		{
			inc = aug(to[i] , min(cap[i] , st) , S , T);
			st -= inc , cap[i] -= inc , cap[i^1] += inc;
			if(!st) break;
		}
	}
	vis[now] = 0;
	return Max - st;
}


bool SPFA(int S,int T)
{
	static queue<int>q;
	static bool inq[maxn];
	memset(dis,0x3f,sizeof dis);
	q.push(T),dis[T] = 0;
	for(int now;!q.empty();)
	{
		now = q.front() , q.pop() , inq[now] = 0;
		for(int i=info[now];i;i=Prev[i])
			if(cap[i^1] && dis[to[i]] > dis[now] + cst[i^1])
			{
				dis[to[i]] = dis[now] + cst[i^1];
				if(!inq[to[i]])
					inq[to[i]] = 1 , q.push(to[i]);
			}
	}
	return dis[S] != dis[0];
}

int MCMF(int S,int T)
{
	cost = 0;
	memcpy(buf,info,sizeof info);
	int stm = 0;
	for(;SPFA(S,T);)
	{
		stm += aug(S,inf,S,T),
		memcpy(info,buf,sizeof buf);
	}
	return cost;
}

int main()
{	
	scanf("%d",&n);
	S = n + 1 , T = n + 2 , SS = n+3 , TT = n+4;
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		while(x--)
		{
			int v;
			scanf("%d",&v);
			Line(i,v,inf,0);
			pcap[i] -- , pcap[v]++;
		}
		Line(S,i,inf,0);
		Line(i,T,inf,0);
	}
	Line(T,S,inf,1);
	for(int i=1;i<=n;i++)
		if(pcap[i] > 0) 
			Line(SS,i,pcap[i],0);
		else 
			if(pcap[i] < 0) 
				Line(i,TT,-pcap[i],0);
	printf("%d\n",MCMF(SS,TT));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值