HDU5483求一个图中的最小生成树不能去掉的边的数目
来源:互联网 发布:淘宝网关键词排名查询 编辑:程序博客网 时间:2024/06/06 04:10
没研究过图论,暂时看不太懂,留着慢慢看吧。
题解:
首先使用Prim算法求出这个图的最小生成树,对于每条非树边都对于着树上的一条链。如果有一条非树边的权值和对应链上的某条树边相等,就可以交换这两条边使得树边不一定在最小生成树上。
所以我们需要对每条树边,看所有覆盖他的非树边的权值有没有和本身相等的,因为非树边的权值不会小于树边,所以只需看覆盖该树边的非树边的最小权值。
这个问题就很传统了,但因为min操作没有逆运算,不能单纯的像求和那样打前缀和的标记。数据范围也不支持树链剖分等做法,但是可以以每个点为根都做一次以下过程,用以根为端点的非树边去覆盖这棵树,这样就没有了逆运算的过程。
综上,时间复杂度为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
- HDU5483求一个图中的最小生成树不能去掉的边的数目
- 求一个图中的环的数目
- BestCoder,Nux Walpurgis,最小生成树必要边的数目
- hdoj 4408 Minimum Spanning Tree 求最小生成树的数目
- poj 2117 Electricity 【无向图求割点】【求去掉一个点后 图中最多的BCC数目】
- 求图的最小生成树
- 求图的最小生成树
- 4.18省选模拟赛T3 道路 求一个图中的每一个最小生成树都必须包含的边的数量
- HDU 5483 Nux Walpurgis 图的最小生成树中必要的边的数目 动态MST问题
- 求树的子树数目
- 求最小生成树的Prim算法
- 求最小生成树的prim算法
- 求最小生成树的Prim算法
- 求最小生成树的Kruskal算法
- prim算法求最小生成树的最大边--poj2253
- KRUSCAL算法求图的最小生成树(C实现)
- prime算法求无向图的最小生成树
- 求图的最小生成树之--Prim算法实现
- UVA 11292 Dragon of Loowater
- 求n^k的前3位和后3位
- 用递归判断一个数组是否递增 JAVA代码
- nyoj 一种排序 8 (结构体 排序)
- Android 事件处理详解(四) —— Handler和AsyncTask(补充)
- HDU5483求一个图中的最小生成树不能去掉的边的数目
- Spinner
- 对Oracle性能调优的基本方案
- web前端,实现上下翻页,支持鼠标滑动翻页h5+css+jq
- leetcode 242 :Valid Anagram
- 计算几何--(半平面判断多边形是否存在内核以及内核面积计算)
- 黑马程序员-----反射
- Java 使用urlconnection下载文件或图片并保存到本地
- KMP算法(二)