Description

Input
第一行两个整数n和m,表示点与线段的数目。
接下来n行,每行两个整数x和y,表示第i个点的坐标,点从1到n编号。
接下来m行,每行四个整数p,q,V1和V2,表示存在一条从第p个点连向第q个点的线段,激活p->q这个方向的费用为V1,另一个方向费用为V2。
保证若两条线段相交,则交点是它们的公共端点。
Output
输出一行一个正整数,表示最小总激活费用。
Sample Input
4 5
0 0
1 0
0 1
1 1
1 2 0 0
1 3 0 3
2 3 1 0
2 4 2 0
4 3 0 0
Sample Output
3
HINT

对于100%的数据,n≤3000,区域数不超过1000,点坐标绝对值不超过1W,每条边激活费用不超过100。
题解:真是一道神题,学到了不少新姿势√
首先,不知道对偶图的请自行百度。因为这道题要用到平面图转对偶图。首先,找一条没有经过的单向边,开始深搜,直到回到自己的点为止,这样就围成了一个区域,在回溯时给经过的每条边都标上这个区域的编号。然后求它的对偶图。这就转成了一个有向图,在上面求最小树形图即可。
代码如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
using namespace std;
namespace Graph
{
#define N 2005
#define M 50005
int X[M],Y[M],val[M],c2;
int nex[M],to[M],hd[N],cnt,tot;
int in[N],fa[N],n;
bool vis[N],inc[N];
void add(int x,int y,int v)
{
X[++c2]=x,Y[c2]=y,val[c2]=v;
}
void addedge(int x,int y)
{
to[++cnt]=y,nex[cnt]=hd[x],hd[x]=cnt;
}
int dfs(int x)
{
inc[x]=1;
if(inc[fa[x]]){tot+=in[x];return fa[x];}
else
{
int tmp=dfs(fa[x]);
if(tmp && x!=tmp)
{
tot+=in[x];
return tmp;
}
else if(x==tmp)
{
tot+=in[x];
return 0;
}
}
inc[x]=0;
return 0;
}
int check()
{
for(int i=0;i<=n;i++)
if(!vis[i]) in[i]=inf,hd[i]=0,inc[i]=1;
for(int i=1;i<=c2;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);
queue<int> q;
q.push(0);
while(!q.empty())
{
int x=q.front();q.pop();
inc[x]=0;
for(int i=hd[x];i;i=nex[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 solve()
{
int ans,sum=0;
for(int i=1;i<=c2;i++) sum+=val[i];
for(int i=1;i<=n;i++) add(0,i,sum+1);
tot=0;
while((ans=check())==-1)
{
for(int i=1;i<=c2;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(int 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 3005
#define M 15000
struct Point
{
int x,y;
Point(){}
Point(int _x,int _y):x(_x),y(_y){}
}a[N];
struct Line
{
int x,y,id;
bool v;
double k;
inline bool operator<(const Line& rhs) const {return k<rhs.k;}
}l[M];
int nex[M],to[M],num[M],hd[N],tot;
bool v[M],vis[N];
int val[M][2],X[M][2];
void add(int x,int y,int nm,bool vv)
{
to[++tot]=y,num[tot]=nm,v[tot]=vv,nex[tot]=hd[x],hd[x]=tot;
}
int dfs(int x,int fa)
{
if(vis[x]) return ++Graph::n;
for(int i=hd[x];i;i=nex[i])
{
if(to[i]==fa)
{
i=nex[i];
if(i) return X[num[i]][v[i]]=dfs(to[i],x);
else return X[num[hd[x]]][v[hd[x]]]=dfs(to[hd[x]],x);
}
}
return 0;
}
void solve()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&l[i*2-1].x,&l[i*2-1].y,&val[i][0],&val[i][1]);
l[i*2-1].id=i,l[i*2-1].v=0;
l[i*2-1].k=atan2(a[l[i*2-1].y].y-a[l[i*2-1].x].y,a[l[i*2-1].y].x-a[l[i*2-1].x].x);
l[i*2].x=l[i*2-1].y,l[i*2].y=l[i*2-1].x,l[i*2].id=i,l[i*2].v=1;
l[i*2].k=atan2(a[l[i*2-1].x].y-a[l[i*2-1].y].y,a[l[i*2-1].x].x-a[l[i*2-1].y].x);
}
sort(l+1,l+m*2+1);
for(int i=1;i<=m*2;i++) add(l[i].x,l[i].y,l[i].id,l[i].v);
for(int i=1;i<=n;i++)
{
vis[i]=1;
for(int j=hd[i];j;j=nex[j])
if(!X[num[j]][v[j]]) X[num[j]][v[j]]=dfs(to[j],i);
vis[i]=0;
}
for(int 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::solve();
printf("%d\n",Graph::solve());
return 0;
}
平面图对偶图与最小树形图

本文介绍了一种解决特定平面图问题的方法,通过构造对偶图并求解最小树形图来找到最小总激活费用。文章详细解释了如何通过深度优先搜索寻找区域并标记边,进而转换成有向图求解。
508

被折叠的 条评论
为什么被折叠?



