【SDUTOJ 2414】An interesting game(最小费用最大流)

【SDUTOJ 2414】An interesting game(最小费用最大流)

An interesting game

Time Limit: 2000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

Xiao Ming recently designs a little game, in front of player there are N small hillsides put in order, now Xiao Ming wants to increase some hillsides to block the player, so he prepared another M hillsides, but he does not hope it will be too difficult,so only K in M hillsides are selected to add at most. Paying attention to the original N hillsides, between each two can add only one hillside. Xiao Ming expects players from the starting place to reach the destination in turn and passes all the hillsides to make his distance travelled longest. Please help Xiao Ming how to add the hillsides that he prepared. Note: The distance between two hillsides is the absolute value of their height difference.

输入

The first line of input is T, (1 <= T <= 100) the number of test cases. Each test case starts with three integers N,M,K (2 <= N <= 1000, 1 <= M <= 1000, 1 <= K <= M and 1 <= K < N), which means that the number of original hillsides, the number of hillsides Xiao Ming prepared and The number of most Xiao Ming can choose from he prepared. Then follow two lines, the first line contains N integers Xi (0 <= Xi <= 30), denoting the height of each original hillside, Note: The first integer is player's starting place and the last integer is player's destination. The second line contains M integers Yi (0 <= Yi <= 30), denoting the height of prepared each hillsides.

输出

For every test case, you should output "Case k: " first in a single line, where k indicates the case number and starts from 1. Then print the distance player can travel longest.

示例输入

32 1 16 982 1 16 9153 2 15 9 1521 22

示例输出

Case 1: 3Case 2: 15Case 3: 36

提示

 

来源

 2012年"浪潮杯"山东省第三届ACM大学生程序设计竞赛

示例程序

真真没看出来是个费用流……又丢失了一次比赛中写流算法的机会。。。TOT

学以致用学以致用,学了却不知道该用这个是什么感觉……

当知道是费用流问题的时候。。。。我的口老血


知道是流量问题了,就比较好办点了,建图跑。建图略复杂,不过也不难写。

先来发题意:n个山包,每个山包有个高度,现在要从最左边走到最右边,两个山包间的路程是两山包的高度差(上山/下山) 然后有m个额外的山包,要求从里面挑k个加到图中,让这样走一遍后路程最长。要求两个山包间只能加一个新山包,两端不可添加。


这样对于初状态可以跑一遍,得到一个总路程。这样每当在i和i+1间添加山包j时,其实就是在总路程里加上abs(high[i]-high[j])+abs(high[j]-high[i+1])-abs(high[i]-high[i+1])

也就是去掉之前的路程,加上新加山包后的路程。


这样再加一个源点与汇点,源点与所有的新山包连接,流量1,费用0。所有的初始山包与汇点连接,流量1,费用0。新山包与初始山包间两两连接,流量1,费用就是刚才的式子。这样你会发现,只需要1~n-1的初始山包。同时如果所有新山包做点,其实可以优化下,因为只有0~30这几种高度,每个高度做点,跟源点间流量变成这种高度的山包数量即可。


这样其实还没完。。跑出来的是不限制k的情况下。再来个流量限制,在刚才的源点前再加个真`源点和它相连,费用0,流量k,这样就达到了限流的效果。


代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread() freopen("data1.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Edge
{
	int v,f,w,next;
};

Edge eg[66666];
int head[2333],h[2333],hcnt[2333];
int dis[2333],pre[2333];
bool vis[2333];
int tp,ans,minf;

void Add(int u,int v,int w,int f)
{
	eg[tp].v = v;
	eg[tp].w = w;
	eg[tp].f = f;
	eg[tp].next = head[u];
	head[u] = tp++;
}

bool bfs(int st,int en)
{
	memset(dis,-1,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(pre,-1,sizeof(pre));
	queue <int> q;
	q.push(st);
	dis[st] = 0;
	minf = INF;
	int u,v,w,f;

	while(!q.empty())
	{
		u = q.front();
		q.pop();
		vis[u] = 0;

		for(int i = head[u]; i != -1; i = eg[i].next)
		{
			v = eg[i].v;
			w = eg[i].w;
			f = eg[i].f;
			if(f && (dis[v] == -1 || dis[v] < dis[u]+w))
			{
				dis[v] = dis[u]+w;
				minf = min(f,minf);
				pre[v] = i;
				if(!vis[v])
				{
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}

	if(pre[en] == -1) return false;

	ans += dis[en];
	for(int i = pre[en]; i != -1; i = pre[eg[i^1].v])
	{
		eg[i].f -= minf;
		eg[i^1].f += minf;
	}

	return true;
}

int main()
{

	int t,n,m,k,x;
	scanf("%d",&t);
	for(int z = 1; z <= t; ++z)
	{
		scanf("%d%d%d",&n,&m,&k);
		memset(head,-1,sizeof(head));
		memset(hcnt,0,sizeof(hcnt));
		tp = 0;
		ans = 0;
		for(int i = 0; i < n; ++i)
		{
			scanf("%d",&h[i]);
			if(i) ans += abs(h[i]-h[i-1]);
		}
		while(m--)
		{
			scanf("%d",&x);
			hcnt[x]++;
		}

		//n为起点 0~n-2表示初始第i个山爬到i+1山路程 n+1~n+30表示高1~30的山数 n+31表示起点前的一个点(限流) n+32表示终点
		Add(n+33,n+31,0,k);
		Add(n+31,n+33,0,0);
		for(int i = 0; i <= 30; ++i)
		{
			if(!hcnt[i]) continue;
			Add(n+31,n+i,0,hcnt[i]);
			Add(n+i,n+31,0,0);
			for(int j = 0; j < n-1; ++j)
			{
				Add(n+i,j,abs(h[j]-i)+abs(h[j+1]-i)-abs(h[j]-h[j+1]),hcnt[i]);
				Add(j,n+i,-abs(h[j]-i)-abs(h[j+1]-i)+abs(h[j]-h[j+1]),0);
			}
		}
		for(int j = 0; j < n-1; ++j)
		{
			Add(j,n+32,0,1);
			Add(n+32,j,0,0);
		}
		while(bfs(n+33,n+32));
		printf("Case %d: %d\n",z,ans);
	}

	return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值