Time Limit: 1000MS | Memory Limit: 131072K | |
Total Submissions: 12833 | Accepted: 3717 |
Description
After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.
With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.
Input
The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between nodei and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.
Output
For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy
’.
Sample Input
4 6 0 6 4 6 0 0 7 20 1 2 1 3 2 3 3 4 3 1 3 2 4 3 0 0 1 0 0 1 1 2 1 3 4 1 2 3
Sample Output
31.19 poor snoopy
Source
#include<cstdio>
#include<cstring>
#include<cmath>
#define inf 2000000000
/*inf可以更大一点*/
#define N 200
#define M 2000
#define NN 50000
using namespace std;
struct Fiona
{
double x,y;
}s[N];/*点*/
struct Syndra
{
int u,v;
double w;
}e[NN];/*边*/
int pre[N],f[N],visit[N];/*前驱,缩点时属于的点,缩点时的一个环判断*/
double l[N];/*l[i],i的最小弧长度*/
double dist(Fiona A,Fiona B)
{
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
/*求坐标之间直线距离*/
}
double Directed_MST(int root,int n,int m)
{
int i,j,k;
int u,v,cnt;
double ans=0;
while(1)
{
cnt=0;
memset(f,0,sizeof(f));
memset(visit,0,sizeof(visit));
/*1.求每个点的最小入边(求最小弧集)*/
for(i=1;i<=n;i++)l[i]=inf;/*清最大值(这里清的2*10^9其实不是最大值,但是也够了)*/
for(i=1;i<=m;i++)
{
u=e[i].u;v=e[i].v;
if(l[v]>e[i].w&&u!=v)pre[v]=u,l[v]=e[i].w;/*更新最小弧*/
}
/*若某个非起点的点没有前驱(最小弧),
即这个点无法通过别的某个点到达,
则图非联通,则返回一个否定值↓↓↓*/
for(i=1;i<=n;i++)if(l[i]==inf&&i!=root)return -1;
l[root]=0;/*因为下面的循环要加上每个点的最小弧长度,而源点不要要入边,所以清一下*/
for(i=1;i<=n;i++)
{
ans+=l[i];
v=i;
while(visit[v]!=i&&!f[v]&&v!=root)
{/*从当前点往前驱找,直到找到一个环(visit[v]!=i)
或者找到了源点,即无法接着找前驱了
或者找到了某个已经被标记完的环(!f[v])*/
visit[v]=i;/*不能在此处写visit[v]=1,然后判断!visit[v],
因为在找的时候visit的作用是一个环回来,
判断找到了一个环,而非是遍历过了*/
/*如 ①
↓
⑦ ←⑥ ←② ←⑤
↑ ↓ ↑
⑧ →⑨ ③ →④
除了①到②长度100000,其他都小于10可以吧?
然后手调一下就“理解深刻”了。
ps:如果整个图是一个个三角环组成点连成的链,
那么是不是可以比较有效地卡一下这个版本的朱刘算法实现呢?
*/
v=pre[v];/*向前遍历寻找*/
}
if(v!=root&&!f[v])
{/*注意此处的“!f[v]”没有虽然不会错,
但是这会导致对环内每个点重复标记,
一方面略有效卡评测,一方面调试时看缩点会很恶心*/
f[v]=++cnt;
for(u=pre[v];u!=v;u=pre[u])f[u]=cnt;
/*这里随意写了,只要保证该在一个环的在一个环就行*/
}
}
if(!cnt)break;/*此处判断有多少环,即进行了几次缩点,若无法再缩(cnt==0)则跳出*/
for(i=1;i<=n;i++)if(!f[i])f[i]=++cnt;/*把剩余的点标记*/
for(i=1;i<=m;i++)
{
u=e[i].u;e[i].u=f[u];
v=e[i].v;e[i].v=f[v];
if(f[u]!=f[v])e[i].w-=l[v];
/*O(m)对每条边进行权值缩减处理*/
/*为什么要缩减呢?
如: ①
↓
⑦ ←⑥ ←② ←⑤
↑ ↓ ↑
⑧ →⑨ ③ →④
除了①到②长度100000,其他都小于10。
我们来手模拟一遍 :
当第一次缩点完成,即2345被缩好后,
我们的ans实际代表该图中除了① → ②以外所有边的权值和
而真正的答案应该是除了⑤ → ②以外所有边的权值和。
这样我们把②的所有入边权值减去⑤ → ②的长度,
使得下次ans+边权的时候加的是(w【① → ②】-w【⑤ → ②】)
即使得初始加的“⑤ → ②”边权值被删掉。(略有点网络流反向弧的感觉)
*/
}
n=cnt;/*因为缩点了,所以我们需要修改一个点的总数*/
root=f[root];
}
return ans;
}
int main()
{
freopen("test.in","r",stdin);
int i,n,m;
double ans;
while(scanf("%d%d",&n,&m)!=EOF)
{
/*此处的加点加边我不赘述了*/
for(i=1;i<=n;i++)scanf("%lf%lf",&s[i].x,&s[i].y);
for(i=1;i<=m;i++)
{
scanf("%d%d",&e[i].u,&e[i].v);
if(e[i].u!=e[i].v)e[i].w=dist(s[e[i].u],s[e[i].v]);
else i--,m--;//除去自边
}
ans=Directed_MST(1,n,m);
if(ans==-1)printf("poor snoopy\n");
else printf("%.2f\n",ans);/*poj double 要用%f */
}
return 0;
}
/*******************************************************************
再附个测试数据:
0 30
0 5
0 13
0 5
1 3
3 1
2 1
2 4
4 2
3 4
2 3
5 9
0 0
0 15
0 19
0 19
0 22
1 2
1 3
1 4
2 3
2 4
3 2
3 5
4 1
5 2
5 8
0 1
0 27
0 0
0 40
0 30
1 3
2 4
3 1
3 4
3 5
4 1
4 5
5 2
5 12
0 2
0 24
0 2
0 34
0 17
2 4
4 4
1 3
2 4
4 4
3 5
1 4
3 4
4 2
5 5
2 2
1 2
4 12
0 0
0 3
0 4
0 6
1 2
1 3
1 4
2 1
2 3
2 4
3 1
3 2
3 4
4 1
4 2
4 3
26.00
47.00
47.00
6.00