题目描述:
求N<3,000个点的稠密图的最小生成树的每条边的最佳替换边。
算法分析:
先求最小生成树,这个不用说了。
dp[i][j]表示,i和i的子孙到j的最小边(不计算生成树的边)。
这个可以通过树形DP(中序遍历 + 后序遍历)求的。
注意j不能是i的子孙,这个要通过时间戳来判断。
同时用一个优先级队列,把这样的边都存起来。在回溯过程中仅用O(logE)的时间就可以判断最佳边了。
复杂度O(V*V*logE)
写起来不太好写,想到不难,但是复杂度不好把握。
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
// graph
const int V = 3000+10;
int n,m, num[V][V];
// prim
int low[V], vis[V], P[V], G[V][V];
const int inf = ~0u>>2;
int prim(){
for(int i=0;i<n;i++) vis[i] = 0, low[i] = inf, P[i] = -1;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
G[i][j] = 0;
P[0] = low[0] = 0;
int ans = 0;
for(int i=0;i<n;i++){
int s , mn = inf;
for(int j= 0; j< n; j++) if(!vis[j] && low[j] < mn){
mn = low[j]; s = j;
}
vis[s] = 1;
ans += mn;
if(P[s] != s) G[s][P[s]] = G[P[s]][s] = mn;
for(int v = 0; v < n; v++) if(num[s][v])
if(!vis[v] && low[v] > num[s][v] )
low[v] = num[s][v], P[v] =s;
}
return ans;
}
// dfs
template <typename T> inline void chkmin(T &a, const T b){if(a>b)a=b;};
int pre[V],snd[V], tm, __ans[V][V];
typedef pair<int, int > node;
priority_queue<node, vector<node> , greater <node> > Q[V];
int temp[V][V];
void dfs1(int u,int f){
pre[u] = tm ++;
for(int v = 0; v< n; v++) if(G[u][v] && pre[v] == -1)
dfs1(v,u);
snd[u] = tm ++;
}
#define ff first
#define ss second
void dfs(int u,int f) {
while(!Q[u].empty()) Q[u].pop();
for(int v=0;v<n;v++) if( G[u][v] && v!=f) {
dfs(v,u);
if(!Q[v].empty()) __ans[u][v] = __ans[v][u] = Q[v].top().ff;
while(!Q[v].empty()) {
int val = Q[v].top().ff;
int to = Q[v].top().ss;
chkmin(temp[u][to],val);
Q[v].pop();
}
}
for(int i=0;i<n;i++) if(i!=f){
chkmin(temp[u][i],num[u][i]);
}
for(int v=0;v<n;v++) if(temp[u][v] != inf ) {
if(pre[v] >= pre[u] && snd[v] <= snd[u]) continue;
Q[u].push(make_pair(temp[u][v],v));
}
}
void work(){
tm = 0;
for(int i=0;i<n;i++)
pre[i] = -1;
dfs1(0,0);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
temp[i][j] = __ans[i][j] = inf;
dfs(0,0);
}
// main
int main(){
while(~scanf("%d%d",&n,&m) && n) {
int u,v,c;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) num[i][j] = inf;
for(int i=0;i<m;i++) {
scanf("%d%d%d",&u,&v,&c);
num[u][v] = num[v][u] = c;
}
int t = prim();
work();
// query
int q; double ans = 0;
scanf("%d",&q);
for(int oo=0;oo<q;oo++){
scanf("%d%d%d",&u,&v,&c);
if(!G[u][v])
ans += t;
else if(c <= __ans[u][v])
ans += t - G[u][v] + c;
else ans += t - G[u][v] + __ans[u][v];
}
printf("%.4lf\n", ans/q);
}
return 0;
}
posted on 2012-07-30 13:46
西月弦 阅读(405)
评论(0) 编辑 收藏 引用 所属分类:
解题报告