BZOJ 1930 SHOI 2003 pacman 吃豆豆 费用流

题目大意:给出一些平面上的点,你有两个吃豆人,从一个点出发,这个吃豆人可以吃到当前点右上方的点。问这两个吃豆人最多可以吃到多少豆子。

思路:我已經吧不相交的条件去掉了。。

不加优化的费用流模型很明显

超级源->源 flow2 cost0

汇->超级汇 flow2 cost0

下面是拆点

i << 1 -> i << 1|1 flow1 cost1

对于从点i能够到达点j的情况

i << 1|1 - > j << 1 flow1 cost0

然后跑朴素费用流,很明显T掉了。一组能够卡的数据,所有点都是(i,i),2000个点跑这个算法会出现n^2条边。

优化的出发点也是很明显的。如果有三个点i,j,k能够从i到j到k,那么我们就肯定不会走i->k这条边,那么这个边就不用加进去。

还有一些小细节,详见代码。

CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 4010
#define MAXE 4100000
#define INF 0x3f3f3f3f
#define S 0
#define _S 1
#define T (MAX - 1)
#define _T (MAX - 2)
using namespace std;

struct Point{
	int x,y;

	bool operator <(const Point &a)const {
		if(x == a.x)	return y < a.y;
		return x < a.x;
	}
	void Read() {
		scanf("%d%d",&x,&y);
	}
}point[MAX];

struct MinCostMaxFlow{
	int head[MAX],total;
	int next[MAXE],aim[MAXE],flow[MAXE],cost[MAXE];

	int f[MAX],from[MAX],p[MAX];
	bool v[MAX];

	MinCostMaxFlow() {
		total = 1;
	}
	void Add(int x,int y,int f,int c) {
		next[++total] = head[x];
		aim[total] = y;
		flow[total] = f;
		cost[total] = c;
		head[x] = total;
	}
	void Insert(int x,int y,int f,int c) {
		Add(x,y,f,c);
		Add(y,x,0,-c);
	}
	bool SPFA() {
		static queue<int> q;
		while(!q.empty())	q.pop();
		memset(f,0x3f,sizeof(f));
		memset(v,false,sizeof(v));
		f[S] = 0;
		q.push(S);
		while(!q.empty()) {
			int x = q.front(); q.pop();
			v[x] = false;
			for(int i = head[x]; i; i = next[i])
				if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
					f[aim[i]] = f[x] + cost[i];
					if(!v[aim[i]])
						v[aim[i]] = true,q.push(aim[i]);
					from[aim[i]] = x;
					p[aim[i]] = i;
				}
		}
		return f[T] != INF;
	}
	int EdmondsKarp() {
		int re = 0;
		while(SPFA()) {
			int max_flow = INF;
			for(int i = T; i != S; i = from[i])
				max_flow = min(max_flow,flow[p[i]]);
			for(int i = T; i != S; i = from[i]) {
				flow[p[i]] -= max_flow;
				flow[p[i]^1] += max_flow;
			}
			re += f[T] * max_flow;
		}
		return re;
	}
}solver;

int cnt;

int main()
{
	cin >> cnt;
	for(int i = 1; i <= cnt; ++i)
		point[i].Read();
	sort(point + 1,point + cnt + 1);
	for(int i = 1; i <= cnt; ++i) {
		int _min = INF;
		for(int j = i + 1; j <= cnt; ++j) {
			if(point[j].y < _min && point[j].y >= point[i].y)
				solver.Insert(i << 1|1,j << 1,2,0);
			if(point[j].y >= point[i].y)
				_min = min(_min,point[j].y);
		}
	}
	for(int i = 1; i <= cnt; ++i) {
		solver.Insert(i << 1,i << 1|1,1,-1);
		solver.Insert(i << 1,i << 1|1,1,0);
		solver.Insert(_S,i << 1,1,0);
		solver.Insert(i << 1|1,_T,1,0);
	}
	solver.Insert(S,_S,2,0);
	solver.Insert(_T,T,2,0);
	cout << -solver.EdmondsKarp() << endl;
	return 0;
}

时间: 2024-08-06 02:06:03

BZOJ 1930 SHOI 2003 pacman 吃豆豆 费用流的相关文章

BZOJ 1930 Shoi2003 pacman 吃豆豆 费用流

题目大意:给定一个平面上的一些点,吃豆先生从原点出发,只能向右或向上走,求两个吃豆先生最多吃到多少豆 每个点拆成两个,之间连一条流量为1,费用为1的边: 如果从一个点出发可以到达另一个点,就将前一个点的出点连向后一个点的入点 跑费用流.但是这样显然是会TLE的 如果i能到j,j能到k,那么显然无需连i->k这条边 这是一个剪枝 加了这个剪枝之后可能会WA 因此还要考虑一个点经过多次的情况 即每个点从入点向出点再连一条流量为1,费用为0的边 加了这个之后就能过了 剪枝不强 但是没有什么情况能把这个

BZOJ1930 [Shoi2003]pacman 吃豆豆 费用流

题目大意:在二维平面上有若干个点,求出两条不相交的二维LIS,使得上面包含的点的数目最多. 思路1:暴力建图 注意到不相交这个条件根本没用,画图可以发现如果相交的话,我们总可以通过交换一些点使得两个序列不相交. 那么问题转化为求出两个没有公共点的上升子序列,使得长度之和最大. 对于这种情况我们利用最大费用流求解. 设(a,b)分别表示一条有向边的流量和费用. S->S' (2,0) S'->x(1,0),x'->T(1,0)(1<=x<=n) x->x' (1,1) 表

BZOJ1930 [Shoi2003]pacman 吃豆豆

SHOI出过这么鬼的题?..跪 首先我们可以想到费用网络流,找一个流量为2的最大费用流即可,问题是怎么建图 因为针对点才有收益,而且收益只有一次,所以考虑拆点,不妨设一个点p拆成入点p1和出点p2 则p1 -> p2连边流量为1,费用为1:再连边流量为1,费用为0:S向p1连边流量为1,费用为0:p2向T连边,流量为1,费用为0 若p能到达q,则p2 -> q1连边,流量为2,费用为0 然后会发现边太多...优化? 如果i能到达j,j能到达k,则i不要向k连边,于是就是一个单调性的问题,对点按

bzoj 3597 [Scoi2014] 方伯伯运椰子 - 费用流 - 二分答案

题目传送门 传送门 题目大意 给定一个费用流,每条边有一个初始流量$c_i$和单位流量费用$d_i$,增加一条边的1单位的流量需要花费$b_i$的代价而减少一条边的1单位的流量需要花费$a_i$的代价.要求最小化总费用减少量和调整次数的比值(至少调整一次). 根据基本套路,二分答案,移项,可以得到每条边的贡献. 设第$i$条边的流量变化量为$m_i$,每次变化花费的平均费用为$w_i$.那么有 $\sum c_id_i - \sum (c_i + m_i)d_i + |m_i|(w_i + mi

BZOJ 2324: [ZJOI2011]营救皮卡丘( floyd + 费用流 )

昨晚写的题...补发一下题解... 把1~N每个点拆成xi, yi 2个. 预处理i->j经过编号不超过max(i,j)的最短路(floyd) S->0(K, 0), S->xi(1, 0)(从i点继续走), 0->yi(1, distance(0->i))(从0出发), xi->yi(1, distance(i->j))(i点走向j点), yi->T(1, 0)(每个点必须经过至少一次), 然后跑最小费用最大流, 费用即为答案. 写完这道题感觉...只是会

BZOJ 3197 Sdoi2013 assassin 动态规划+树同构+费用流

题目大意:给定一棵树和两组权值,求第一组权值最少改变多少个之后这棵树经过重标号之后与第二组权值相同 这个题做法很神- - 首先和3162一样的处理方式 我们先找到这棵树的重心作为根 如果重心有两个就新建一个根连向这两个重心 令f[x][y]表示x所在子树的第一组权值和y所在子树的第二组权值匹配的最小花销 转移的必要条件是x所在的子树与y所在的子树同构且x与y深度相同 为了保证无后效性,x的所有子节点与y的所有子节点之间的最小花销必须都已求出 那么我们将节点以深度的倒数为第一键值,Hash值为第二

[BZOJ 1834][ZJOI2010]network 网络扩容(费用流)

Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用.求: 1. 在不扩容的情况下,1到N的最大流: 2. 将1到N的最大流增加K所需的最小扩容费用. Solution 先求出最大流maxflow 求最小扩容费用的话,对于每一条边,建一条容量为c费用为0的边,再建一条容量为INF费用为w的边 跑费用流求流入maxflow+k的费用 #include<iostream> #include<cstdio> #include

bzoj 3280: 小R的烦恼(费用流)

3280: 小R的烦恼 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 300  Solved: 154 [Submit][Status][Discuss] Description 小R最近遇上了大麻烦,他的程序设计挂科了.于是他只好找程设老师求情.善良的程设老师答应不挂他,但是要求小R帮助他一起解决一个难题. 问题是这样的,程设老师最近要进行一项邪恶的实验来证明P=NP,这个实验一共持续n天,第i天需要a[i]个研究生来给他搬砖.研究生毕竟也是

BZOJ.3638.CF172 k-Maximum Subsequence Sum(模拟费用流 线段树)

题目链接 各种zz错误..简直要写疯 /* 19604kb 36292ms 朴素线段树:线段树上每个点维护O(k)个信息,区间合并时O(k^2),总O(mk^2logn)->GG 考虑费用流:建一条n+1个点的链(点权设在边上,故需n+1个点),链上每个点和S.T连边,相邻点连边 这样数列中的区间和每条增广路一一对应 每次最多增广k次,O(nmk)->still GG 考虑费用流这一过程的实质:每次增广相当于贪心,本质上只有两种情况: 选取一段(新增一个区间).从已选的某个区间中删除一段 使用