POJ 1715 - Highways

- POJ 1715 -

Highways

Time Limit: 1000MS | Memory Limit: 10000K
Special Judge

题意:

给定一个整数 n,表示有 n 个城镇,编号为1~n,接下来 n 行是各个城镇的坐标,然后给定一个整数 m ,表示已修建好的路,接下来 m 行,每行两个整数,分别是已修建好的路连通的两个城镇,找出一条单线使得所有城镇与公路总长度尽可能短的连接起来,每一条公路都应该打印出该公路连接的城镇编号,并用空格隔开。若所有城镇都已连通,则输出为空(换行)。

数据范围:

N (1 <= N <= 750),M (0 <= M <= 1000),坐标大小<=10000

解题思路:

Prim 算法

代码:
Memory: 4604K | Time: 110MS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x3f3f3f
#define zero 1e-7

typedef long long ll;
const int N=755;
double mp[N][N], w[N];
int x[N], y[N], dis[N];
bool vis[N];

void build(int n) {
    for(int i=1; i<=n; i++) {
        for(int j=i; j<=n; j++) {
            double d=sqrt((double)((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
            mp[i][j]=mp[j][i]=d;
        }
    }
    return ;
}

void prim(int n) {
    memset(vis, false, sizeof(vis));
    vis[1]=true;
    for(int i=1; i<=n; i++) {
        w[i]=mp[1][i];
        dis[i]=1;
    }
    for(int i=1; i<n; i++) {
        double mind=INF;
        int temp;
        for(int j=2; j<=n; j++) {
            if(w[j]<mind && !vis[j]) {
                mind=w[j];
                temp=j;
            }
        }
        vis[temp]=true;
        if(mind)
            printf("%d %d\n", dis[temp], temp);
        for(int j=2; j<=n; j++) {
            if(mp[temp][j]<w[j] && !vis[j]) {
                w[j]=mp[temp][j];
                dis[j]=temp;
            }
        }
    }
}

int main() {
    int n, m, a, b;
    scanf("%d", &n);//居然只有一组输入o(╥﹏╥)o
    for(int i=1; i<=n; i++)
        scanf("%d %d", &x[i], &y[i]);
    build(n);
    scanf("%d", &m);
    while(m--) {
        scanf("%d %d", &a, &b);
        mp[a][b]=mp[b][a]=0;
    }
    prim(n);
}

不晓得为啥,用 Kruskal 算法写的 TLE 了,本来一开始是觉得用 Prim 写比较合适,但是因为误以为是多组输入,写成了 while(scanf("%d", &n)!=EOF),所以就改用 Kruskal 写了,还是 TLE ,后来发现是一组输入后就改了过来,结果还是 TLE ,绝望之际把原来用 Prim 写的改了一下提交,谢天谢地终于 AC 了(╥╯﹏╰╥)ง,决定还是把用 Kruskal 写的 TLE 的代码附上,说不定日后能看出什么端倪。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x3f3f3f
#define zero 1e-7

typedef long long ll;
const int N=755;
double mp[N][N];
int x[N], y[N];
int par[N];

struct node {
    int from, to;
    double w;
}edge[N*N/2];

bool cmp(node a, node b) {
    return a.w<b.w;
}

void build(int n) {
    for(int i=1; i<n; i++) {
        for(int j=i+1; j<=n; j++) {
            double d=sqrt((double)((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
            mp[i][j]=d;
        }
    }
    return ;
}

void init(int n) {
    for(int i=1; i<=n; i++)
        par[i]=i;
    return ;
}

int find(int a) {
    if(a==par[a]) return a;
    else return par[a]=find(par[a]);
}

void kruskal(int cnt, int n) {
    sort(edge, edge+cnt, cmp);
    //for(int i=0; i<=n; i++) printf("%d-%d: edge[%d]=%lf\t", edge[i].from, edge[i].to, i, edge[i].w);
    bool flag=false;
    for(int i=0; i<cnt; i++){//需要连通的边数为n
        int a=find(edge[i].from);
        int b=find(edge[i].to);
        //printf("a=%d, b=%d\t", a, b);
        if(a!=b) {
            par[b]=a;
            n--;
            if(edge[i].w>0) {
                printf("%d %d\n", edge[i].from, edge[i].to);
                flag=true;
            }
        }
        if(!n) break;
    }
    if(!flag) printf("\n");
}

int main() {
    int n, m, a, b, cnt;
    scanf("%d", &n);
    cnt=0;
    init(n);
    for(int i=1; i<=n; i++)
        scanf("%d %d", &x[i], &y[i]);
    build(n);
    scanf("%d", &m);
    while(m--) {
        scanf("%d %d", &a, &b);
        mp[a][b]=mp[b][a]=0;
    }
    for(int i=1; i<n; i++) {
        for(int j=i+1; j<=n; j++) {
            edge[cnt].from=i;
            edge[cnt].to=j;
            edge[cnt++].w=mp[i][j];
        }
    }
   kruskal(cnt, n-1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值