图论——最小生成树

poj 3723(Kruskal)

从给出的数据可以看出,其不一定是树,更可能是森林,并且根据题意是要求边的总和最大化。故相对的挑选Kruskal算法,而不是prim,只需将权值改为负数即可等效为求最小生成树。
同时注意,所给的示例中我们得注意到,他有可能给同样的girl和boy不同的数值,如第二组测试数据中既有 “2 4 9820”,又有“2 4 8326”,这时我们要挑最大的那个值作为边的权值存储。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 50050
int father[MAX], son[MAX];
int v, l;

typedef struct Kruskal { //存储边的信息
    int a;
    int b;
    int value;
};

bool cmp(const Kruskal & a, const Kruskal & b) {
    return a.value < b.value;
}

int unionsearch(int x) { //查找根结点+路径压缩
    return x == father[x] ? x : unionsearch(father[x]);
}

bool join(int x, int y) { //合并
    int root1, root2;
    root1 = unionsearch(x);
    root2 = unionsearch(y);
    if(root1 == root2) //为环
        return false;
    else if(son[root1] >= son[root2]) {
        father[root2] = root1;
        son[root1] += son[root2];
    } else {
        father[root1] = root2;
        son[root2] += son[root1];
    }
    return true;
}

int main() {
    int ncase, ltotal, sum, flag,boy , girl;
    Kruskal edge[MAX];
    scanf("%d", &ncase);//原先用cin超时了
    //cin>>ncase;
    while(ncase--) {
        scanf("%d%d%d", &girl, &boy, &l);
        //cin>>girl>>boy>>l;
        v = girl + boy;
        ltotal = 0, sum = 0, flag = 0;
        for(int i = 0; i <= v; ++i) { //初始化
            father[i] = i;
            son[i] = 1;
        }
        for(int i = 1; i <= l ; ++i) {//初始化
            edge[i].value = 0;
        }
        for(int i = 1; i <= l ; ++i) {

            int A,B,V;
            //cin>>A>>B>>V;
            scanf("%d%d%d", &A, &A, &V);
            if(V > -edge[i].value) {//使所输入的边权值最大
                edge[i].a = A;
                edge[i].b = B+girl;
                edge[i].value = -V;

            }
        }
        sort(edge + 1, edge + 1 + l, cmp); //按权值由小到大排序
        for(int i = 1; i <= l; ++i) {
            if(join(edge[i].a, edge[i].b)) {
                ltotal++; //边数加1
                sum += edge[i].value; //记录权值之和
                //cout<<edge[i].a<<"->"<<edge[i].b<<" = "<<edge[i].value<<endl;
            }
        }
        printf("%d\n", sum+10000*v);
    }
    return 0;
}

代码比较耗时,还有可以改进的地方。仅供参考!

poj 1789(prim)
题意:

数据中给出的每行字符串代表一种货车,货车可由其他货车“衍生”,现在问:在最佳“衍生”关系的条件下,求“ the highest possible quality of a derivation plan. ”,即“衍生”关系中父子的字符串中不同的字母的个数和。
以测试数据为例:

这里写图片描述

如上图所示,当2,3,4都被1“衍生”出来时便是最佳“衍生”关系,此时的 the highest possible quality of a derivation plan. ”就等于1/3。

代码如下:

#include<iostream>
#include<string.h>
#include<fstream>
using namespace std;
#define Max_N 2002
char truck[Max_N][8];
int dis[Max_N][Max_N] = {0};
int N;
#define inf 0x3f3f3f3f
bool vis[Max_N];
int lowc[Max_N];
void find_dis() {//找出各顶点间的关系
    for(int i = 0; i<N; i++) {
        for(int j=i; j<N; j++) {
            int temp_num = 0;
            if(i == j) {
                dis[i][j] = 7;
                continue;
            }
            for(int k =0; k<7; k++) {

                if(truck[i][k] != truck[j][k]) {
                    temp_num++;
                } 
            }
            dis[i][j] = temp_num;
            dis[j][i] = temp_num;
        }
    }
}
int prim() { 
    int i, j, p;
    int minc, res = 0;
    memset(vis, 0, sizeof(vis));
    vis[0] = 1;
    for (i=1; i<N; i++) lowc[i] = dis[0][i];
    for (i=1; i<N; i++) {
        minc = inf;
        p = -1;
        for (j=0; j<N; j++)
            if (0 == vis[j] && minc > lowc[j]) {
                minc = lowc[j];
                p = j;
            }
            if (inf == minc) return -1; // 原图不连通
            res += minc;
            vis[p] = 1;
            for (j=0; j<N; j++)
                if (0 == vis[j] && lowc[j] > dis[p][j])
                    lowc[j] = dis[p][j];
    }
    return res;
}

int main() {
    //ifstream in("C://a.txt",std::ios::in);
    memset(truck,NULL,sizeof(truck));
    while(cin>>N , N!= 0) {
        cin.getline(truck[0],8);
        for(int i=0; i<N; i++) {
            cin.getline(truck[i],8);
        }
        find_dis();
        cout<<"The highest possible quality is 1/"<<prim()<<"."<<endl;
    }
}

如有错误,还望指出!
转载请注明出处:http://blog.csdn.net/big_heart_c

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值