poj1789 Truck History(最小生成树)

2018-3-24

简单的最小生成树问题。

题目大意是:
给你n个字符串,他们的distance就是串中不同字符的个数,要求算出所有串的distance’s 最小 sum ;

#include<iostream>
#include<cstring>
#define MAX 10
using namespace std;

const int N = 2000, M = 7;
char x[N+1][M+1];
int dis[N+1][N+1],D[N+1];
bool z[N+1];
int n;

int getDis(int i,int j){
    int s=0;
    for (int k=0;x[i][k]!='\0';k++){
        if (x[i][k]!=x[j][k]) s++;
    }
    return s;
}

int prim(){
    int i,j,s=0;
    for (j=0;j<n;j++){
        D[j]=dis[0][j];
    }
    z[0]=1;
    int min_path,min_point;
    for (i=1;i<n;i++){
        min_path=MAX;
        for (j=0;j<n;j++){
            if (z[j]) continue;
            if (D[j]<min_path){
                min_path=D[j];
                min_point=j;
            }
        }
        D[min_point]=MAX;
        z[min_point]=1;
        s+=min_path;
        for (j=0;j<n;j++){
            if (z[j]) continue;
            if (dis[min_point][j]<D[j]){
                D[j]=dis[min_point][j];
            }
        }
    }
    return s;
}

int main(){
    int i,j;
    while (cin>>n&&n){
        for (i=0;i<n;i++){
            cin>>x[i];
        }
        for (i=0;i<n;i++){
            for (j=0;j<n;j++){
                dis[i][j]=MAX;
            }
        } 
        int d;
        memset(z,false,sizeof(z));
        for (i=0;i<n-1;i++){
            for (j=i+1;j<n;j++){
                d=getDis(i,j);
                dis[i][j]=d;
                dis[j][i]=d;
            }
        }
        cout<<"The highest possible quality is 1/"<<prim()<<"."<<endl;
    }
    return 0;
}

直接使用prim算法求解即可,需要注意的是,输出的最后有一个小点。

或者你也可以用kruskal算法

#include<iostream>
#include<cstring>
#include<algorithm>
#define MAX 10
using namespace std;

const int N = 2000, M = 7;
char x[N+1][M+1];
int f[N+1];
int n,k;

struct xd{
    int s,e,v;
}dis[N*N/2];

bool cmp(struct xd a,struct xd b){
    return a.v<b.v;
}

int getDis(int i,int j){
    int s=0;
    for (int k=0;x[i][k]!='\0';k++){
        if (x[i][k]!=x[j][k]) s++;
    }
    return s;
}

int find(int p){
    int q=p;
    while (p!=f[p]){
        p=find(f[p]);
    }
    while (q!=p){
        f[q]=p;
        q=f[q];
    }
    return p;
}

int kruskal(){
    int i,s=0;
    for (i=0;i<n;i++){
        f[i]=i;
    }
    for (i=0;i<k;i++){
        int ii=find(dis[i].s),jj=find(dis[i].e);
        if (ii!=jj){
            f[ii]=jj;
            s+=dis[i].v;
        }
    }
    return s;
}

int main(){
    int i,j,d;
    while (cin>>n&&n){
        for (i=0;i<n;i++){
            cin>>x[i];
        }
        k=0;
        for (i=0;i<n-1;i++){
            for (j=i+1;j<n;j++){
                d=getDis(i,j);
                dis[k].s=i;
                dis[k].e=j;
                dis[k].v=d;
                k++;
            }
        }
        sort(dis,dis+k,cmp);
        cout<<"The highest possible quality is 1/"<<kruskal()<<"."<<endl;
    }
    return 0;
}

注意并查集路径的压缩。

——————————————————————————————————————————————————-

2018-5-22

prime算法求解:

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;

const int N = 2000, M = 7;
char x[N+1][M+1];
int d[N+1][N+1];
int dis[N+1];
bool f[N+1];
int n,res;

int cal(int i,int j){
    int cnt=0;
    for (int k=0;k<M;k++){
        if (x[i][k]!=x[j][k]){
            cnt++;
        }
    }
    return cnt;
}

void init(){
    for (int i=0;i<n;i++){
        for (int j=0;j<n;j++){
            int tmp=cal(i,j);
            d[i][j]=tmp;
            d[j][i]=tmp;
        }
    }
}

void prime(){
    memset(f,false,sizeof(f));
    for (int i=0;i<n;i++){
        dis[i]=inf;
    }
    for (int j=0;j<n;j++){
        dis[j]=d[0][j];
    }
    res=0;
    f[0]=true;
    for (int i=0;i<n-1;i++){
        int min_path=inf,min_point=0;
        for (int j=0;j<n;j++){
            if (!f[j]&&dis[j]<min_path){
                min_path=dis[j];
                min_point=j;
            }
        }
        f[min_point]=true;
        res+=min_path;
        for (int j=0;j<n;j++){
            if (!f[j]&&d[min_point][j]<dis[j]){
                dis[j]=d[min_point][j];
            }
        }
    }
    printf ("The highest possible quality is 1/%d.\n",res);
}

int main(){
    while(scanf ("%d",&n)!=EOF){
        if (n==0) break;
        for (int i=0;i<n;i++){
            scanf ("%s",x[i]);
        }
        init();
        prime();
    }
    return 0;
}
有一个可以优化的地方,就是我们在计算距离的时候,j是可以从i+1开始的,
因为这里是一个无向图。

2018-5-23

kruskal算法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;

const int N = 2000, M = 7;
char p[N+1][M+1];
int f[N+1];
int n,res,cnt;

struct lu{
    int s,e,v;
}x[N*N/2];

int cal(int i,int j){
    int sum=0;
    for (int k=0;k<M;k++){
        if (p[i][k]!=p[j][k]){
            sum++;
        }
    }
    return sum;
}

bool cmp(struct lu a,struct lu b){
    return a.v<b.v;
}

void init(){
    cnt=0;res=0;
    for (int i=0;i<n;i++){
        f[i]=i;
    }
    for (int i=0;i<n-1;i++){
        for (int j=i+1;j<n;j++){
            int tmp=cal(i,j);
            x[cnt].s=i;
            x[cnt].e=j;
            x[cnt].v=tmp;
            cnt++;
        }
    }
    sort(x,x+cnt,cmp);
}

int find(int i){
    int j=i;
    while (i!=f[i]){
        i=f[i];
    }
    while (j!=f[j]){
        j=f[j];
        f[j]=i;
    }
    return i;
}

void kruskal(){
    for (int i=0;i<cnt;i++){
        int ii=find(x[i].s),jj=find(x[i].e);
        if (ii!=jj){
            res+=x[i].v;
            f[ii]=f[jj];       
        }
    }
    printf ("The highest possible quality is 1/%d.\n",res);
}

int main(){
    while(scanf ("%d",&n)!=EOF){
        if (n==0) break;
        for (int i=0;i<n;i++){
            scanf ("%s",p[i]);
        }
        init();
        kruskal();
    }
    return 0;
}
需要注意的就是并查集那边的路径压缩问题。

这两种算法的区别是,prime算法比较适用于图边比较多的情况,因为我们是用的邻接矩阵存的图,而kruskal算法比较适用于图边比较少的情况,因为我们是用的结构体存的图,算法的时间复杂度与边数有关。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值