题目大意:给出一个平面图,这个平面图中分布着一些点,可以用平面图中的边将一些点围住,问围住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;
}