生成树


POJ 1639 最小度限制生成树 http://poj.org/problem?id=1639

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctype.h>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
#define maxn 22


int n,k;

int e1[maxn][maxn];
int e2[maxn][maxn];

struct Edge{
    int u,v,w;
    Edge(int a,int b,int c){u=a,v=b,w=c;}
    bool friend operator< (Edge a,Edge b){
        return a.w>b.w;
    }
};
priority_queue<Edge> q;

char name[maxn][12];
int l;
char s1[12],s2[12];
int get_id(char *p){
    for(int i=0;i<l;i++){
        if(strcmp(p,name[i])==0)
            return i;
    }
    strcpy(name[l],p);
    l++;
    return l-1;
}

int bin[maxn];
int find(int a){
    if(bin[a]!=a) bin[a]=find(bin[a]);
    return bin[a];
}
bool get(int a,int b){
    int x=find(a);
    int y=find(b);
    if(x!=y) {bin[y]=x;return 1;}
    return 0;
}

void dfs(int u,int fa){
    for(int i=1;i<l;i++){
        if(e2[u][i]==1&&i!=fa){
            get(u,i);
            dfs(i,u);
        }
    }
}

void Print_ans(){
    int ans=0;
    for(int i=0;i<l;i++)
        for(int j=i+1;j<l;j++)
            if(e2[i][j]==1)
                ans+=e1[i][j];
    printf("Total miles driven: %d\n",ans);
}

void solve(){
    int cnt=0;

    for(int i=0;i<=l;i++)  bin[i]=i;
    while(!q.empty()){
        Edge e=q.top();
        q.pop();
        if(get(e.u,e.v)){
            if(e.u==0||e.v==0) cnt++;
            e2[e.u][e.v]=e2[e.v][e.u]=1;
        }
    }


    for(int i=0;i<=l;i++) bin[i]=i;
    for(int j=1;j<l;j++){
        if(e2[0][j]==1) dfs(j,0);
    }

    while(cnt>k){
        int x,y,mmin=1000000;
        for(int i=1;i<l;i++){
            for(int j=i+1;j<l;j++){
                if(e1[i][j]!=0&&e2[i][j]==0){
                    int i1=find(i);
                    int j1=find(j);
                    if(i1==j1) continue;
                    int mmax=max(e1[0][i1],e1[0][j1]);
                    if(mmin>(e1[i][j]-mmax)){
                        mmin=e1[i][j]-mmax;
                        x=i; y=j;
                    }
                }
            }
        }
        cnt--;
        e2[x][y]=1,e2[y][x]=1;
        int x1=find(x),y1=find(y);
        if(e1[0][x1]>e1[0][y1]) e1[0][x1]=e1[x1][0]=0,get(y1,x1);
        else e1[0][y1]=e1[y1][0]=0,get(x1,y1);
    }

    Print_ans();
}

void init(){
    strcpy(name[0],"Park");
    l=1;
    memset(e1,0,sizeof(e1));
    memset(e2,0,sizeof(e2));
    while(!q.empty()) q.pop();

    for(int i=0;i<n;i++){
        int w;
        scanf("%s%s%d",s1,s2,&w);
        int d1=get_id(s1);
        int d2=get_id(s2);
        q.push(Edge(d1,d2,w));
        e1[d1][d2]=e1[d2][d1]=w;
    }
    scanf("%d",&k);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        init();
        solve();
    }
}



HDU 2121最小树形图(无固定根) http://acm.hdu.edu.cn/showproblem.php?pid=2121

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const double eps = 1e-6;
const double inf = ~0u>>1;
typedef long long LL;

using namespace std;

const int N = 1100;   // 调整
const int M = 20010;  // 调整

struct node {  //可删
    double x, y;
} point[N];

double SQ(int u, int v) {  // 可删
    return sqrt((point[u].x - point[v].x)*(point[u].x - point[v].x) +
                (point[u].y - point[v].y)*(point[u].y - point[v].y));
}

struct edg {
    int u, v;
    int cost;  //double
} E[M];

int In[N]; //double
int ID[N];
int vis[N];
int pre[N];
int NV, NE;  // n,m    0-->n-1  0-->m-1

int Minroot;// 求入点

int Directed_MST(int root) {  //double
    int ret = 0;   //double
    int i, u, v;
    while(true) {
        REP(i, NV)   In[i] = inf;
        REP(i, NE) {    //找最小入边
            u = E[i].u;
            v = E[i].v;
            if(E[i].cost < In[v] && u != v) {
                In[v] = E[i].cost;
                if(u==root) Minroot=i;     //求入口
                pre[v] = u;
            }
        }
        REP(i, NV) {    //如果存在除root以外的孤立点,则不存在最小树形图
            if(i == root)   continue;
            //printf("%.3lf ", In[i]);
            if(In[i] == inf)    return -1;
        }

        int cnt = 0;
        CL(ID, -1);
        CL(vis, -1);
        In[root] = 0;

        REP(i, NV) {    //找环
            ret += In[i];
            int v = i;
            while(vis[v] != i && ID[v] == -1 && v != root) {
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && ID[v] == -1) {  //重新标号
                for(u = pre[v]; u != v; u = pre[u]) {
                    ID[u] = cnt;
                }
                ID[v] = cnt++;
            }
        }
        if(cnt == 0)    break;
        REP(i, NV) {
            if(ID[i] == -1) ID[i] = cnt++;    //重新标号
        }
        REP(i, NE) {    //更新其他点到环的距离
            v = E[i].v;
            E[i].u = ID[E[i].u];
            E[i].v = ID[E[i].v];
            if(E[i].u != E[i].v) {
                E[i].cost -= In[v];
            }
        }
        NV = cnt;
        root = ID[root];
    }
    return ret;
}
int main() {
    while(scanf("%d%d",&NV,&NE)!=EOF)
    {
        int x=NE;
        for(int i=0;i<NE;i++)
            scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].cost);
        for(int i=0;i<NV;i++){
            E[NE].u=NV;
            E[NE].v=i;
            E[NE].cost=1e7;
            NE++;
        }
        NV++;
        int ans=Directed_MST(NV-1);
        ans-=1e7;
        if(ans>1e7) printf("impossible\n\n");
        else printf("%d %d\n\n",ans,Minroot-x);  // Minroot-x 入口 
    }
}

/*
无固定根的最小树形图 
Sample Input

3 1 
0 1 1 
4 4 
0 1 10
0 2 10 
1 3 20 
2 3 30

 
Sample Output

impossible 
40 0      // 40权值和  0 入口
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值