HDU5483求一个图中的最小生成树不能去掉的边的数目

来源:互联网 发布:淘宝网关键词排名查询 编辑:程序博客网 时间:2024/06/06 04:10

没研究过图论,暂时看不太懂,留着慢慢看吧。

题解:

首先使用Prim算法求出这个图的最小生成树,对于每条非树边都对于着树上的一条链。如果有一条非树边的权值和对应链上的某条树边相等,就可以交换这两条边使得树边不一定在最小生成树上。

所以我们需要对每条树边,看所有覆盖他的非树边的权值有没有和本身相等的,因为非树边的权值不会小于树边,所以只需看覆盖该树边的非树边的最小权值。

这个问题就很传统了,但因为min操作没有逆运算,不能单纯的像求和那样打前缀和的标记。数据范围也不支持树链剖分等做法,但是可以以每个点为根都做一次以下过程,用以根为端点的非树边去覆盖这棵树,这样就没有了逆运算的过程。

综上,时间复杂度为O(n^2)O(n2)

大神代码1:使用的trajan算法:
#include <stdio.h>#include <string.h>#include <algorithm>#include <vector>const int N = 3000 + 5;std::vector<std::pair<int,int> > w[N];int head[N];int n;struct Edge {    int v,next;} g[3000 * 3000 + 5];int etot;inline void add_edge(int u,int v) {    g[etot].v = v; g[etot].next = head[u]; head[u] = etot ++;}int dfn[N],low[N],tim;int trajin(int u,int pree) {    int ret = 0;    dfn[u] = low[u] = ++ tim;    for (int i = head[u]; i != -1; i = g[i].next) {        int v = g[i].v;        if ((pree ^ i) == 1) continue;        if (!dfn[v]) ret += trajin(v,i);        low[u] = std::min(low[u],low[v]);    }    if (dfn[u] == low[u]) {        if (pree != -1)            ret ++;    }    return ret;}int dsu[N];int find(int x) {    return dsu[x] == x ? x : dsu[x] = find(dsu[x]);}int work() {    int ret = 0;    for (int i = 0; i < n; ++ i) {        dsu[i] = i;        head[i] = -1;    }    for (int x = 1; x <= 3000; ++ x) {        if (w[x].empty()) continue;        etot = 0;        for (int i = 0; i < w[x].size(); ++ i) {            int a = w[x][i].first;            int b = w[x][i].second;            a = find(a);            b = find(b);            if (a == b) continue;            add_edge(a,b);            add_edge(b,a);            dfn[a] = low[a] = 0;            dfn[b] = low[b] = 0;        }        tim = 0;        for (int i = 0; i < w[x].size(); ++ i) {            int a = w[x][i].first;            int b = w[x][i].second;            a = find(a);            b = find(b);            if (a == b) continue;            if (dfn[a] == 0)                ret += trajin(a,-1);            if (dfn[b] == 0)                ret += trajin(b,-1);        }        for (int i = 0; i < w[x].size(); ++ i) {            int a = w[x][i].first;            int b = w[x][i].second;            a = find(a);            b = find(b);            if (a == b) continue;            head[a] = -1;            head[b] = -1;            dsu[a] = b;        }    }    return ret;}int main() {    int cas;    scanf("%d",&cas);    while (cas--) {        scanf("%d",&n);        for (int i = 1; i <= 3000; ++ i) {            w[i].clear();        }        for (int i = 0; i < n; ++ i) {            for (int j = i + 1; j < n; ++ j) {                int x;                scanf("%d",&x);                w[x].push_back(std::make_pair(i,j));            }        }        printf("%d\n",work());    }}

大神代码2:

使用的dfs,

#include<stdio.h>#include<string.h>#include<algorithm>#include<vector>using namespace std;#define scan(x) scanf("%d",&(x))#define scan2(x,y) scanf("%d%d",&(x),&(y))#define scan3(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))#define scan4(x,y,z,k) scanf("%d%d%d%d",&(x),&(y),&(z),&(k))const int maxn = 3003;const int maxm = maxn * maxn;const int inf = 1000000000;int n, m;__int64 mst;int map[maxn][maxn];int dp[maxn][maxn], best[maxn][maxn];int dis[maxn], pre[maxn];bool vis[maxn];vector<int> edge[maxn];#include<iostream>int minz(int a, int b){    return a < b ? a : b;}void init(){    int i, j;    for(i = 0; i < n; i++)    {        for(j = 0; j < n; j++)            map[i][j] = dp[i][j] = inf;        edge[i].clear();        vis[i] = 0;        pre[i] = -1;        dis[i] = inf;    }}void input(){//cerr<<"st"<<endl;//    int x, y, z;//    while(m--)//    {//        scanf("%d%d%d", &x, &y, &z);//        cerr<<x<<" "<<y<<" "<<z<<endl;//        map[x][y] = map[y][x] = z;//    }        for(int i=0;i<n;i++){                for(int j=i+1;j<n;j++){                        int w;scan(w);                        map[i][j] = map[j][i] = w;                        //cerr<<i<<" "<<j<<" "<<w<<endl;                }        }}void prim(){    int i, j, k;    for(i = 1; i < n; i++)    {        dis[i] = map[0][i];        pre[i] = 0;    }    dis[0] = inf;    vis[0] = 1;    pre[0] = -1;    mst = 0;    for(i = 0; i < n-1; i++)    {        k = 0;        for(j = 1; j < n; j++)            if(!vis[j] && dis[k] > dis[j])                k = j;        vis[k] = 1;        mst += dis[k];       //建最小生成树        if(pre[k] != -1)            edge[k].push_back(pre[k]),            edge[pre[k]].push_back(k);        for(j = 1; j < n; j++)            if(!vis[j] && dis[j] > map[k][j] )                dis[j] = map[k][j], pre[j] = k;    }}int dfs1(int u, int fa, int rt) // 求 点rt 到 以u为根的数及其子树的最小距离{    int i;    for(i = 0; i < edge[u].size(); i++)    {        int v = edge[u][i];        if(v == fa) continue;        dp[rt][u] = minz(dp[rt][u], dfs1(v, u, rt));    }    if(fa != rt) dp[rt][u] = minz(dp[rt][u], map[rt][u]);    return dp[rt][u];}int dfs2(int u, int fa, int rt) // 求 以rt为根的数及其子树 到 以u为根的数及其子树的最小距离{    int i;    int ans = dp[u][rt];    for(i = 0; i < edge[u].size(); i++)    {        int v = edge[u][i];        if(v == fa) continue;        ans = minz(ans, dfs2(v, u, rt));    }    return ans;}void solve(){    int i,j;    for(i = 0; i < n; i++)        dfs1(i, -1, i);    for(i = 0; i < n; i++)        for(j = 0; j < edge[i].size(); j++)        {            int v = edge[i][j];            best[i][v] = best[v][i] = dfs2(v, i, i);        }}void query(){//    int x, y, z;//    double sum = 0;//    scanf("%d", &m);//    x=1,y=3,z=10;//        if(pre[x] != y && pre[y] != x)//            sum += mst * 1.0;//        else//            sum += mst * 1.0 - map[x][y] + minz(best[x][y], z);//    printf("%.4f\n", sum/1);    int ans =0 ;    for(int i=0;i<n;i++){        int x=i;        for(int j=0;j<edge[i].size() ; j++){                int y=edge[i][j];                if(x<y) continue;                int sum = mst - map[x][y] + best[x][y];                //cerr<<"best:"<<best[x][y]<<endl;                if(sum != mst){                        ans++;                }        }    }    printf("%d\n",ans);}//int main()//{//freopen("C:/OJ/in.txt","r",stdin);//    while( ~scanf("%d%d", &n, &m) && n + m)//    {//        init();//        input();//        prim();//        solve();//        query();//    }//    return 0;//}int main(){        #ifndef ONLINE_JUDGE        freopen("C:/OJ/in.txt","r",stdin);    #endif    int T;scan(T);    while(T--){                scan(n);                m=n-1;                init();                input();               // cerr<<"start:"<<map[0][1]<<endl;                prim();                //std::cerr<<mst<<std::endl;                solve();                query();               // cerr<<"end:"<<map[0][1]<<endl;    }}


0 0
原创粉丝点击