bzoj-2960 跨平面

141 篇文章 0 订阅
75 篇文章 0 订阅

题意:

给出一个平面图,每条边从不同方向各有一个跨越代价(或者不能跨越);

求一个最小总跨越代价,使从某个平面区域能跨越到所有其他区域;

即在此平面图的对偶图上求最小树形图;

平面图中点数<=3000,区域数<=1000,跨越代价<=100;


题解:

实际上这是两道裸题。。。然后我作死的学了两种算法;


首先是平面图转对偶图:

首先将无向边拆成两条单向边,按顺/逆时针挂在端点上;

每一次从一条没有经过的单向边出发,深搜;

深搜每到达一个点,我们都找到从来的边顺/逆时针旋转,得到的第一个边继续深搜,搜到最开始的点时停止;

这个过程中因为每次都是找第一条边来搜,所以这个区域一定不包含其他区域的;

然后记录该区域编号,深搜回溯时将来时的每条边都记录这个编号;

搜索次数与平面区域数有关,每条边只会被搜索一次,而平面区域数与边数最多与顶点数同级;

所以如果使用二分查找,时间复杂度是O(nlogn)的吧。。。

(然而我这么懒当然写的是O(n^2)暴力)


转化成对偶图之后,就是应用最小树形图的朱刘算法了;

这个网上讲的似乎都很详细?

需要注意的地方就是找环的时候小心一些,不要将环外面的树也算进去;

当然了,这道题没有自环,不用做预处理;

时间复杂度是O(nm);


代码:


#include<queue>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
namespace Graph
{
	#define N 2100
	#define M 50000
	int X[M],Y[M],val[M],ce;
	int next[M],to[M],head[N],cnt,tot;
	int in[N],fa[N];
	int n;
	bool vis[N],inc[N];
	void add(int x,int y,int v)
	{
		Y[++ce]=y;
		X[ce]=x;
		val[ce]=v;
	}
	void addedge(int x,int y)
	{
		to[++cnt]=y;
		next[cnt]=head[x];
		head[x]=cnt;
	}
	int dfs(int x)
	{
		inc[x]=1;
		if(inc[fa[x]])
		{
			tot+=in[x];
			return fa[x];
		}
		else
		{
			int temp=dfs(fa[x]);
			if(temp&&x!=temp)
			{
				tot+=in[x];
				return temp;
			}
			else if(x==temp)
			{
				tot+=in[x];
				return 0;
			}
		}
		inc[x]=0;
		return 0;
	}
	int check()
	{
		for(int i=0;i<=n;i++)
			if(!vis[i])
				in[i]=0x3f3f3f3f,head[i]=0,inc[i]=1;
		for(int i=1;i<=ce;i++)
		{
			if(!vis[X[i]]&&!vis[Y[i]])
			{
				if(in[Y[i]]>val[i])
				{
					in[Y[i]]=val[i];
					fa[Y[i]]=X[i];
				}
			}
		}
		int ret=0;
		for(int i=1;i<=n;i++)
			if(!vis[i])
				ret+=in[i];
		cnt=0;
		for(int i=1;i<=n;i++)
			if(!vis[i])
				addedge(fa[i],i);
		static queue<int>q;
		q.push(0);
		while(!q.empty())
		{
			int x=q.front();
			q.pop();
			inc[x]=0;
			for(int i=head[x];i;i=next[i])
			{
				q.push(to[i]);
			}
		}
		for(int i=0;i<=n;i++)
			if(!vis[i]&&inc[i])
			{
				memset(inc,0,sizeof(inc[0])*(n+1));
				dfs(i);
				return -1;
			}
		return ret;
	}
	int slove()
	{
		int ans,sum,i;
		for(i=1,sum=0;i<=ce;i++)
		{
			sum+=val[i];
		}
		for(i=1;i<=n;i++)
			add(0,i,sum+1);
		tot=0;
		while((ans=check())==-1)
		{
			for(i=1;i<=ce;i++)
			{
				if(inc[X[i]]&&!vis[Y[i]]&&!inc[Y[i]])
				{
					X[i]=n+1;
				}
				else if(!vis[X[i]]&&!inc[X[i]]&&inc[Y[i]])
				{
					val[i]-=in[Y[i]];
					Y[i]=n+1;
				}
			}
			for(i=0;i<=n;i++)
			{
				if(inc[i])
					vis[i]=1,inc[i]=0;
			}
			n++;
		}
		return ans+tot-sum-1;
	}
	#undef N
	#undef M
}
namespace Plane
{
	#define N 3100
	#define M 15000
	struct Point
	{
		int x,y;
		Point(){}
		Point(int _,int __):x(_),y(__){}
	}a[N];
	struct Line
	{
		int x,y,num;
		bool v;
		double alpha;
		friend bool operator <(Line a,Line b)
		{
			return a.alpha<b.alpha;
		}
	}l[M];
	int next[M],to[M],num[M],head[N],ce;
	bool v[M];
	int	val[M][2],X[M][2];
	bool vis[N];
	void add(int x,int y,int tnum,bool tv)
	{
		to[++ce]=y;
		v[ce]=tv;
		num[ce]=tnum;
		next[ce]=head[x];
		head[x]=ce;
	}
	int dfs(int x,int pre)
	{
		if(vis[x])	return ++Graph::n;
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]==pre)
			{
				i=next[i];
				if(i)
					return X[num[i]][v[i]]=dfs(to[i],x);
				else
					return X[num[head[x]]][v[head[x]]]=dfs(to[head[x]],x);
			}
		}
		return 0;
	}
	void slove()
	{
		int n,m,i,x,y;
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
		{
			scanf("%d%d",&a[i].x,&a[i].y);
		}
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d%d",&x,&y,&val[i][0],&val[i][1]);
			l[i+i-1].x=x,l[i+i-1].y=y,l[i+i-1].num=i,l[i+i-1].v=0;
			l[i+i-1].alpha=atan2(a[y].y-a[x].y,a[y].x-a[x].x);
			l[i+i].x=y,l[i+i].y=x,l[i+i].num=i,l[i+i].v=1;
			l[i+i].alpha=atan2(a[x].y-a[y].y,a[x].x-a[y].x);
		}
		sort(l+1,l+m+m+1);
		for(i=1;i<=m+m;i++)
		{
			add(l[i].x,l[i].y,l[i].num,l[i].v);
		}
		for(x=1;x<=n;x++)
		{
			vis[x]=1;
			for(i=head[x];i;i=next[i])
			{
				if(!X[num[i]][v[i]])
					X[num[i]][v[i]]=dfs(to[i],x);
			}
			vis[x]=0;
		}
		for(i=1;i<=m;i++)
		{
			if(val[i][0])
				Graph::add(X[i][0],X[i][1],val[i][0]);
			if(val[i][1])
				Graph::add(X[i][1],X[i][0],val[i][1]);
		}
	}
	#undef N
	#undef M
}
int main()
{
	Plane::slove();
	printf("%d\n",Graph::slove());
	return 0;
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值