Pandaland
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 745 Accepted Submission(s): 166
Problem Description
Mr. Panda lives in Pandaland. There are many cities in Pandaland. Each city can be treated as a point on a 2D plane. Different cities are located in different locations.
There are also M bidirectional roads connecting those cities. There is no intersection between two distinct roads except their endpoints. Besides, each road has a cost w.
One day, Mr. Panda wants to find a simple cycle with minmal cost in the Pandaland. To clarify, a simple cycle is a path which starts and ends on the same city and visits each road at most once.
The cost of a cycle is the sum of the costs of all the roads it contains.
Input
The first line of the input gives the number of test cases, T. T test cases follow.
Each test case begins with an integer M.
Following M lines discribes roads in Pandaland.
Each line has 5 integers x1,y1,x2,y2, w, representing there is a road with cost w connecting the cities on (x1,y1) and (x2,y2)
Output
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the cost Mr. Panda wants to know.
If there is no cycles in the map, y is 0.
limits
∙1≤T≤50.
∙1≤m≤4000.
∙−10000≤xi,yi≤10000.
∙1≤w≤105.
Sample Input
2 5 0 0 0 1 2 0 0 1 0 2 0 1 1 1 2 1 0 1 1 2 1 0 0 1 5 9 1 1 3 1 1 1 1 1 3 2 3 1 3 3 2 1 3 3 3 1 1 1 2 2 2 2 2 3 3 3 3 1 2 2 1 2 2 1 3 2 4 1 5 1 4
Sample Output
Case #1: 8 Case #2: 4
Source
大致题意就是,给你一个图,然后让你在里面找一个最小的环。
说到找最小环,我很惊叹网上居然没有一个很好的算法。见到的算法都是用floyd的O(N^3)或者dijkstra的O(N^2logN)。但是这些显然都不能满足这题的要求。然后其实本题还加了一个条件,就是一定是一个平面图。一开始还拼命地去想平面图的性质,现在发现其实这些都无所谓。
这题,只要你联想到之前多校赛的第k小生成树的和,你就会知道如何下手。那题利用树边和非树边进行修改生成树和成环。这题也可以类似。由于要求是最小环,我们可以首先对这个图求最小生成树。根据树的性质,如果我们再添加上任意一条边,就会形成一个环。至于这个环的大小,我们可以用LCA来处理,计算出树上两点距离再加上边权结果就是这个环的大小。考虑最小环,我们只需要枚举这个边,然后找到最小的环即可。时间复杂度为O(V+E+NlogN),NlogN为排序。
upd:上面的方法是错误的,具体为什么看下面的评论就知道了。不好意思误导大家了,看来这个数据也比较水。正解的话还是下面那段,dijkstra的剪枝。
事后看了下网上的题解,惊叹于居然大家都是dijkstra暴力做的,每次枚举一条边,然后把这条边删掉,边的两端设置为起点和终点,跑dijkstra。在跑的时候,每次dij到出现一个权值比当前ans要大的时候就停止dij。这样也是可以水过的,虽然理论是肯定会T的。具体见代码吧,感觉方法还是非常巧妙的,代码是队友写的,跟我风格不太符合:
#include<bits/stdc++.h>
#define N 8010
#define INF 100000000000
#define LL long long
using namespace std;
typedef pair<int,int> P;
LL i,j,k,l,n,t,h;
map<P,LL>s;
vector <int> a[N],b[N];
bool f[N];
LL L[N],fa[N],anc[N][20],d[N],m;
struct edge
{
int a,b,c;
bool operator < (const edge x)const
{
return c<x.c;
}
}E[N];
void lable(LL x,LL ffa,LL p) //LCA标号
{
for (LL i=0;i<a[x].size();i++)if (a[x][i]!=ffa)
{
d[a[x][i]]=d[x]+b[x][i];
fa[a[x][i]]=x;
L[a[x][i]]=p+1;
lable(a[x][i],x,p+1);
}
}
void preprocess() //倍增预处理
{
for (LL i=1;i<=n;i++)
{
anc[i][0]=fa[i];
for (LL j=1;(1<<j)<=n;j++) anc[i][j]=-1;
}
for (LL j=1;(1<<j)<=n;j++)
for (LL i=1;i<=n;i++)
if (anc[i][j-1]!=-1)
{
LL x=anc[i][j-1];
anc[i][j]=anc[x][j-1];
}
}
LL query(LL p,LL q) //求两点LCA
{
LL log,i;
if (L[p]<L[q]) swap(p,q);
for (log=1;(1<<log)<=L[p];log++);log--;
for (LL i=log;i>=0;i--)
if (L[p]-(1<<i)>=L[q])
{
p=anc[p][i];
}
if (p==q)return p; //LCA?ap
for (LL i=log;i>=0;i--)
if (anc[p][i]!=-1 && anc[p][i]!=anc[q][i])
{
p=anc[p][i];
q=anc[q][i];
}
return fa[p];//LCA?afa[p]
}
int faa[N];
int getfa(int x) //并查集……
{
if (faa[x]==0)return x;else
{
int t=getfa(faa[x]);
faa[x]=t;
return t;
}
}
bool used[N];
int main()
{
LL T,tt=0;
scanf("%I64d",&T);
while (T--)
{
t=0;s.clear();
memset(f,0,sizeof f);memset(d,0,sizeof d);memset(used,0,sizeof used);
memset(a,0,sizeof a);memset(b,0,sizeof b);
memset(faa,0,sizeof faa);memset(fa,0,sizeof fa);
memset(L,0,sizeof L);memset(anc,0,sizeof anc);
m=100000000000;
scanf("%I64d",&n);
for (i=1;i<=n;i++)
{
LL x,y;
scanf("%I64d%I64d",&x,&y);
if (!s[P(x,y)]) {j=++t;s[P(x,y)]=t;}else j=s[P(x,y)];
scanf("%I64d%I64d",&x,&y);
if (!s[P(x,y)]) {k=++t;s[P(x,y)]=t;}else k=s[P(x,y)];
scanf("%I64d",&l);
E[i]=edge{j,k,l};
}
sort(E+1,E+n+1);
for (i=1;i<=n;i++) //确定最小生成树,标记树边
{
int p=getfa(E[i].a),q=getfa(E[i].b);
if (p!=q)
{
used[i]=1;
int v=E[i].a,u=E[i].b;
a[v].push_back(u);b[v].push_back(E[i].c); //建树
a[u].push_back(v);b[u].push_back(E[i].c);
faa[p]=q;
}
}
int nn=n;
n=t;
for (i=1;i<=n;i++)
{
if (fa[i]==0)
{
fa[i]=i;
lable(i,-1,0);
}
}
preprocess();
for (i=1;i<=nn;i++)if (!used[i]) //枚举非树边求最小环
{
int v=E[i].a,u=E[i].b;
LL w=d[v]+d[u]-d[query(v,u)]*2;
if (w+E[i].c<m)m=w+E[i].c;
}
if (m==100000000000) m=0;
printf("Case #%I64d: %I64d\n",++tt,m);
}
return 0;
}