BZOJ 2965 保护古迹 平面图转对偶图+最小割

题目大意:给出一个平面图,这个平面图中分布着一些点,可以用平面图中的边将一些点围住,问围住k个点的最小花费是多少。

思路:这题重点是平面图转对偶图。做法不难理解。先将所有的边拆成两条,枚举所有的边,若这个边没有被标记过,那么就对这条边进行搜索,弄出来以这个边为一边的平面区域,可以顺时针或者逆时针。将所有边挂在这条边的起点上,在所有点上按照每条边的极角排序,每次找的时候找大于(或小于)当前边的反向边的第一条边作为搜索的下一条边。直到回到最开始的点。找边的过程中记录面积,判断面积的正负来判断这个平面区域是有限区域还是无限区域,顺便记录所有的点在那个平面区域内。

第二部分是最小割部分。注意到由于点只有10个,我们可以O(2^n)枚举那些点被保护,源->这些被保护的点,f:INF保证不被隔断。平面图中的边所连接的两个平面区域之间连边,边权是平面图上的边的边权。跑最小割然后不断更新答案即可。

CODE:

#define _CRT_SECURE_NO_WARNINGS

#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1100
#define MAXE 50100
#define S 0
#define T (MAX - 1)
#define INF 0x3f3f3f3f
using namespace std;

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

	int deep[MAX];

	void Reset() {
		total = 1;
		memset(head,0,sizeof(head));
	}
	void Add(int x,int y,int f) {
		next[++total] = head[x];
		aim[total] = y;
		flow[total] = f;
		head[x] = total;
	}
	void Insert(int x,int y,int f) {
		Add(x,y,f);
		Add(y,x,f);
	}
	bool BFS() {
		static queue<int> q;
		while(!q.empty())	q.pop();
		memset(deep,0,sizeof(deep));
		deep[S] = 1;
		q.push(S);
		while(!q.empty()) {
			int x = q.front(); q.pop();
			for(int i = head[x]; i; i = next[i])
				if(flow[i] && !deep[aim[i]]) {
					deep[aim[i]] = deep[x] + 1;
					q.push(aim[i]);
					if(aim[i] == T)	return true;
				}
		}
		return false;
	}
	int Dinic(int x,int f) {
		if(x == T)	return f;
		int temp = f;
		for(int i = head[x]; i; i = next[i])
			if(flow[i] && temp && deep[aim[i]] == deep[x] + 1) {
				int away = Dinic(aim[i],min(temp,flow[i]));
				if(!away)	deep[aim[i]] = 0;
				flow[i] -= away;
				flow[i^1] += away;
				temp -= away;
			}
		return f - temp;
	}
}solver;

struct Line;

struct Point{
	int x,y;

	Point(int _,int __):x(_),y(__) {}
	Point() {}
	Point operator +(const Point &a)const {
		return Point(x + a.x,y + a.y);
	}
	Point operator -(const Point &a)const {
		return Point(x - a.x,y - a.y);
	}
	void Read() {
		scanf("%d%d",&x,&y);
	}
}point[MAX],arch[MAX];
vector<Line *> e[MAX];

inline long long Cross(const Point &p1,const Point &p2)
{
	return (long long)p1.x * p2.y - (long long)p1.y * p2.x;
}

struct Line{
	Point p,v;
	double alpha;
	int flag,x,y,len;

	Line *another;

	Line(Point _,Point __,int ___,int ____,int _____):p(_),v(__),x(___),y(____),len(_____) {
		alpha = atan2(v.y,v.x);
		flag = 0;
	}
	Line() {}
}line[MAX * MAX];

bool out[MAX];

struct Edge{
	int x,y,len;

	Edge(int _,int __,int ___):x(_),y(__),len(___) {
		if(out[y])	y = T;
		if(out[x])	x = T;
	}
	Edge() {}
}edge[MAX * MAX];

inline bool cmp(Line *l1,Line *l2)
{
	return l1->alpha < l2->alpha;
}

inline bool OnLeft(Line *l,const Point &p)
{
	return Cross(l->v,p - l->p) > 0;
}

int archs,points,lines,edges;
int belong[MAX];

inline void Find(Line *l,int p)
{
	static bool v[MAX];
	memset(v,true,sizeof(v));
	int start = l->x,now = l->y;
	long long area = Cross(point[l->x],point[l->y]);
	for(int i = 1; i <= archs; ++i)
		if(v[i])
			v[i] = !OnLeft(l,arch[i]);
	l->flag = p;
	do {
		vector<Line *>::iterator it = upper_bound(e[now].begin(),e[now].end(),l->another,cmp);
		if(it == e[now].end())	it = e[now].begin();
		l = *it;
		now = l->y;
		l->flag = p;
		area += Cross(point[l->x],point[l->y]);
		for(int i = 1; i <= archs; ++i)
			if(v[i])
				v[i] = !OnLeft(l,arch[i]);
	}while(now != start);
	for(int i = 1; i <= archs; ++i)
		if(v[i])
			belong[i] = p;
	if(area > 0)	out[p] = true;
}

inline Line MakeLine(int x,int y,int len)
{
	Line re(point[x],point[y] - point[x],x,y,len);
	return re;
}

inline int MakeGraph(int status)
{
	solver.Reset();
	int re = 0;
	for(int i = 1; i <= archs; ++i)
		if((status >> (i - 1))&1) {
			solver.Insert(S,belong[i],INF);
			++re;
		}
	for(int i = 1; i <= edges; ++i)
		solver.Insert(edge[i].x,edge[i].y,edge[i].len);
	return re;
}

int ans[MAX];

int main()
{
	cin >> archs >> points >> lines;
	for(int i = 1; i <= archs; ++i)
		arch[i].Read();
	for(int i = 1; i <= points; ++i)
		point[i].Read();
	for(int x,y,z,i = 1; i <= lines; ++i) {
		scanf("%d%d%d",&x,&y,&z);
		line[i << 1] = MakeLine(x,y,z);
		line[i << 1|1] = MakeLine(y,x,z);
		line[i << 1].another = &line[i << 1|1];
		line[i << 1|1].another = &line[i << 1];
		e[x].push_back(&line[i << 1]);
		e[y].push_back(&line[i << 1|1]);
	}
	for(int i = 1; i <= points; ++i)
		sort(e[i].begin(),e[i].end(),cmp);
	int blocks = 0;
	for(int i = 2; i <= (lines << 1|1); ++i)
		if(!line[i].flag)
			Find(&line[i],++blocks);
	for(int i = 2; i <= (lines << 1|1); i += 2)
		edge[++edges] = Edge(line[i].flag,line[i^1].flag,line[i].len);
	memset(ans,0x3f,sizeof(ans));
	for(int i = 1; i < (1 << archs); ++i) {
		int cnt = MakeGraph(i),max_flow = 0;
		while(solver.BFS())
			max_flow += solver.Dinic(S,INF);
		ans[cnt] = min(ans[cnt],max_flow);
	}
	for(int i = 1; i <= archs; ++i)
		printf("%d\n",ans[i]);
	return 0;
}

时间: 2025-01-02 02:45:18

BZOJ 2965 保护古迹 平面图转对偶图+最小割的相关文章

【BZOJ2965】保护古迹 平面图转对偶图,暴力,网络流

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43199045"); } 题意:自己看去吧. 题解:如果不考虑这道题的某些小数据范围, 那么正解应该是: 首先平面图转对偶图, 然后扫描线处理名胜古迹 过程中运用到邪恶的平衡树(就算是set也依然恶心) 或者用神奇方法Ⅰ判断(cheat)一个名胜古迹在

BZOJ 4541: [Hnoi2016]矿区 平面图转对偶图+DFS树

4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 433  Solved: 182[Submit][Status][Discuss] Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若 干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点 的线段组成.每个开发区域的矿量与该开发区域的面积有关:具

BZOJ 1934: [Shoi2007]Vote 善意的投票 最小割

1934: [Shoi2007]Vote 善意的投票 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1934 Description 幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉.对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神.虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票.我们定义一次投票的冲突数

BZOJ 1266 AHOI 2006 上学路线route 最小割

题目大意:给出一个无向图,问从1到n的最短路发生变化需要割掉最少花费的边权总值是多少. 思路:先要把所有最短路上的边搞出来,一个Floyd就可以解决,然后把所有在最短路上的边都加到最大流的图中,然后跑最小割就是答案. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 510

【BZOJ 3144】 3144: [Hnoi2013]切糕 (最小割模型)

3144: [Hnoi2013]切糕 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1764  Solved: 965 Description Input 第一行是三个正整数P,Q,R,表示切糕的长P. 宽Q.高R.第二行有一个非负整数D,表示光滑性要求.接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R). 100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超

BZOJ 1001 Beijing 2006 狼抓兔子 最小割

题目大意:有一张无向图,描述的是兔子窝的位置和之间的边.现在狼来抓兔子了,兔子慌忙的从(1,1)逃走到(m,n).每条边上都有能通过最多的兔子数量.狼不想让兔子逃走,每在一条边驻守一只狼就可以避免一个兔子通过.问最少多少狼可以让所有兔子都不能逃走. 思路:建图,按题目中的意思是去掉最小的边使得源到汇不连通,显然的最小割. CODE: #include <queue> #include <cstdio> #include <cstring> #include <io

BZOJ 1266 AHOI2006 上学路线route Floyd+最小割

题目大意:给定一张图,每条边有一个长度和一个花费,要求删掉一些边使1到n的最短路变长,求最小花销 首先求出最短路(用什么求随便,反正数据范围小),然后将所有在最短路上的边连到新图中,求最小割就是答案 图没有重边- - 数组开小WA了半篇- - #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 510 #define S 1 #defi

BZOJ 1976 BeiJing2010组队 能量魔方 Cube 最小割

题目大意 给出一个N×N×N的矩阵,矩阵上的每一个方块可以涂上两种颜色,相邻的两个方块如果涂上了不同的颜色,就会产生一点能量.现在已知了一些方块的颜色,问最多可以产生多少点能量. 思路 假设所有相邻的方块之间全部都产生能量,且不考虑已经上好色的方块,之后减去不合法的就行了. 一般来说这种相邻的方块之间会产生一些什么的一般都是把所有点染色,一种颜色的与S相连,另一种与T相连.这道题中,左侧的点若是最终属于S集,代表这个点上了P色,否则上了N色:右侧的点若是最终属于T集,代表这个点上了P色,否则上了

【平面图】【最小割】【最短路】【Heap-Dijkstra】bzoj1001 [BeiJing2006]狼抓兔子

http://wenku.baidu.com/view/8f1fde586edb6f1aff001f7d.html #include<cstdio> #include<queue> #include<cstring> using namespace std; typedef long long ll; #define N 1001 int n,m,S,T,nn; struct Point{int u,d;}; bool operator < (Point a,Po