首页 > 代码库 > BZOJ 2965 保护古迹 平面图转对偶图+最小割
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; }
BZOJ 2965 保护古迹 平面图转对偶图+最小割
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。