題意:
給出一個平面圖,每條邊從不同方向各有一個跨越代價(或者不能跨越);
求一個最小總跨越代價,使從某個平面區域能跨越到所有其他區域;
即在此平面圖的對偶圖上求最小樹形圖;
平面圖中點數<=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;
}