首页 > 代码库 > BZOJ 2965 保护古迹 平面图转对偶图+最小割

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

题目大意:给定一个平面图以及一些点,求将1个、2个、3个……点围起来所需要的最小代价

首先平面图转对偶图

枚举每个点的每条没有走过的出边进行DFS,每到达一个点之后向来时的边逆/顺时针转到的第一条边继续深搜,这样可以搜出所有的区域(包括最外层的无限区域)

我们可以用面积的符号来判断出最外层的无限区域

接下来我们需要判断一个点在哪个区域,由于点只有10个,因此暴力枚举即可

判断一个点是否在某个简单多边形内的方法是这样的:

从一个点出发沿任意方向引出一条射线

统计这条射线与多边形的交点数

若交点数为奇数则在多边形内,否则则在多边形外

为了防止射线卡到某个点上,可以随机一个夹角/方向向量

如果判断一个点是否在所有向量的同侧会导致无法判断凹多边形的情况,不过数据弱,这么判断也过了

然后就是最小割的问题了= = 由于点最多只有10个,因此我们可以暴力2^10枚举哪些点被保护,更新答案即可。

这题写的真是闹心0- - 断断续续一堆事写了半个星期- -

#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 4040
#define S 0
#define T 4039
#define INF 0x3f3f3f3f
#define PI 3.141592653589793238462643
using namespace std;
int n,m,p,pos[15],ans[15];
namespace Max_Flow{
	struct abcd{
		int to,f,next;
	}table[100100];
	int head[M],tot=1;
	int dpt[M];
	void _Add(int x,int y,int z)
	{
		table[++tot].to=y;
		table[tot].f=z;
		table[tot].next=head[x];
		head[x]=tot;
	}
	void Link(int x,int y,int z)
	{
		_Add(x,y,z);
		_Add(y,x,0);
	}
	void Restore()
	{
		int i;
		for(i=2;i<=tot;i+=2)
		{
			table[i].f+=table[i^1].f;
			table[i^1].f=0;
		}
	}
	void Destroy(int x)
	{
		int i;
		for(i=tot-2*x+1;i<=tot;i+=2)
			table[i].f=0;
	}
	bool BFS()
	{
		static int q[M];
		int i,r=0,h=0;
		memset(dpt,-1,sizeof dpt);
		dpt[S]=1;q[++r]=S;
		while(r!=h)
		{
			int x=q[++h];
			for(i=head[x];i;i=table[i].next)
				if(table[i].f&&!~dpt[table[i].to])
				{
					dpt[table[i].to]=dpt[x]+1;
					q[++r]=table[i].to;
					if(table[i].to==T)
						return true;
				}
		}
		return false;
	}
	int Dinic(int x,int flow)
	{
		int i,left=flow;
		if(x==T) return flow;
		for(i=head[x];i&&left;i=table[i].next)
			if(table[i].f&&dpt[table[i].to]==dpt[x]+1)
			{
				int temp=Dinic(table[i].to,min(left,table[i].f) );
				left-=temp;
				table[i].f-=temp;
				table[i^1].f+=temp;
			}
		if(left) dpt[x]=-1;
		return flow-left;
	}
}
namespace Graph{
	struct Point{
		double x,y;
		Point() {}
		Point(double _,double __):
			x(_),y(__) {}
		friend istream& operator >> (istream &_,Point &p)
		{
			scanf("%lf%lf",&p.x,&p.y);
			return _;
		}
		Point operator + (const Point &p) const
		{
			return Point(x+p.x,y+p.y);
		}
		Point operator - (const Point &p) const
		{
			return Point(x-p.x,y-p.y);
		}
		double operator * (const Point &p) const
		{
			return x*p.y-y*p.x;
		}
		double operator ^ (const Point &p) const
		{
			return x*p.x+y*p.y;
		}
	}points[M],_points[20];
	struct Line{
		Point p1,p2;
		Line() {}
		Line(const Point &_,const Point &__):
			p1(_),p2(__) {}
	};
	struct List{
		List *another;
		int to,f,belong;
		double alpha;
		List(int _,int __,double ___):
			another(0x0),to(_),f(__),belong(0),alpha(___) {}
	};
	int cnt;
	vector<List*> a[M];
	vector<Line> lines;
	bool Compare(List *x,List *y)
	{
		return x->alpha < y->alpha;
	}
	void DFS(int x,List *from)
	{
		from->belong=cnt;
		lines.push_back( Line(points[from->another->to],points[x]) );

		vector<List*>::iterator it;
		it=lower_bound(a[x].begin(),a[x].end(),from->another,Compare);
		it++;if(it==a[x].end()) it=a[x].begin();

		if((*it)->belong)
			return ;
		DFS((*it)->to,*it);
	}
	void Add(int x,int y,int z)
	{
		Point p=points[y]-points[x];

		List *temp1=new List(y,z,atan2(p.y,p.x));
		List *temp2=new List(x,z,atan2(-p.y,-p.x));

		temp1->another=temp2;
		temp2->another=temp1;

		a[x].push_back(temp1);
		a[y].push_back(temp2);
	}
	double Get_Angle(const Point &p1,const Point &p2)
	{
		return acos( (p1^p2)/sqrt(p1^p1)/sqrt(p2^p2) );
	}
	double Get_Area()
	{
		double re=0;
		vector<Line>::iterator it;
		for(it=lines.begin();it!=lines.end();it++)
			re+=(it->p2)*(it->p1);
		return re/2;
	}
	bool In_Section(const Point &p)
	{
		static const Point v(3.141592653,2.718281828);
		int re=0;
		Point _p=p+v;
		vector<Line>::iterator it;
		for(it=lines.begin();it!=lines.end();it++)
			if( ( (p-it->p1)*(_p-it->p1) )*( (p-it->p2)*(_p-it->p2) )<0 &&
				Get_Angle(v,it->p1-p)+Get_Angle(v,it->p2-p)<=PI )
				++re;
		return re&1;
	}
}

int main()
{
	using namespace Graph;
	using namespace Max_Flow;
	int i,j,x,y,z;
	cin>>p>>n>>m;
	for(i=1;i<=p;i++)
		cin>>_points[i];
	for(i=1;i<=n;i++)
		cin>>points[i];
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Add(x,y,z);
	}
	for(i=1;i<=n;i++)
		sort(a[i].begin(),a[i].end(),Compare);
	for(i=1;i<=n;i++)
	{
		vector<List*>::iterator it;
		for(it=a[i].begin();it!=a[i].end();it++)
			if(!(*it)->belong)
			{
				lines.clear();
				
				++cnt,DFS((*it)->to,*it);
				
				if(Get_Area()<0)
				{
					Link(cnt,T,INF);
					continue;
				}
				for(j=1;j<=p;j++)
					if( In_Section(_points[j]) )
						pos[j]=cnt;
			}
	}
	for(i=1;i<=n;i++)
	{
		vector<List*>::iterator it;
		for(it=a[i].begin();it!=a[i].end();it++)
			Link((*it)->belong,(*it)->another->belong,(*it)->f);
	}
	memset(ans,0x3f,sizeof ans);
	for(i=1;i<1<<p;i++)
	{
		int temp=0;
		for(j=1;j<=p;j++)
			if( i&(1<<j-1) )
			{
				++temp;
				Link(S,pos[j],INF);
			}
		int ans=0;
		while( BFS() )
			ans+=Dinic(S,INF);
		::ans[temp]=min(::ans[temp],ans);
		Restore();
		Destroy(temp);
	}
	for(i=1;i<=p;i++)
		printf("%d\n",ans[i]);
	return 0;
}


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