最小树形图---有向图的最小生成树(与无向图不一样QAQ)
朱刘算法(必须从0开始存)
POJ 3164 模板题
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node
{
int x,y;
double z;
}t[100100];
int xi[1100],yi[1100],pre[1100],id[1100],vis[1100];
double in[1100];
int n,m,x,y,z;
double ans;
double zhuliu(int root,int n)
{
double res = 0;
while(1)
{
for(int i = 0;i < n; ++i) in[i] = inf; //存最小边权
for(int i = 0;i < m; ++i) //遍历每条边
if(t[i].y != t[i].x && in[t[i].y] > t[i].z)
{
pre[t[i].y] = t[i].x;
in[t[i].y] = t[i].z;
}
for(int i = 0;i < n; ++i)
if(i != root && in[i] == inf) return -1; //除了根以外有点没有入边
int tot = 0; //找环
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root] = 0;
for(int i = 0;i < n; ++i)
{
res += 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(int u = pre[v]; u != v;u = pre[u])
id[u] = tot;
id[v] = tot++;
}
}
if(tot == 0) break; //建立新图,缩点重新编号
for(int i = 0;i < n; ++i)
if(id[i] == -1) id[i] = tot++;
for(int i = 0;i < m; ++i)
{
int u = t[i].x;
int v = t[i].y;
t[i].y = id[v];
t[i].x = id[u];
if(t[i].x != t[i].y) t[i].z -= in[v];
}
n = tot;
root = id[root];
}
return res;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i = 0;i < n; ++i)
scanf("%d%d",&xi[i],&yi[i]);
for(int i = 0;i < m; ++i)
{
scanf("%d%d",&x,&y);
x--; y--;
t[i].x = x;
t[i].y = y;
t[i].z = sqrt((xi[x] - xi[y]) * (xi[x] - xi[y])
+ (yi[x] - yi[y]) * (yi[x] - yi[y]));
}
ans = zhuliu(0,n);
if(ans == -1) printf("poor snoopy\n");
else printf("%.2f\n",ans);
}
return 0;
}
HDU 2121
无根的最小树形图问题
虚拟一个超级源点,建一条比所有边总和sum多1的边,看最后的结果是否大于2*(sum+1),不是的话就是原图有最小树形图,是的话就代表没有
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node
{
int x,y;
double z;
}t[100100];
int pre[1100],id[1100],vis[1100],in[1100];
int n,m,x,y,z,ans,sum,minr;
double zhuliu(int root,int n)
{
double res = 0;
while(1)
{
for(int i = 0;i < n; ++i) in[i] = inf;
for(int i = 0;i < m; ++i)
if(t[i].y != t[i].x && in[t[i].y] > t[i].z)
{
pre[t[i].y] = t[i].x;
in[t[i].y] = t[i].z;
if(t[i].x == root) minr = i;
}
for(int i = 0;i < n; ++i)
if(i != root && in[i] == inf) return -1;
int tot = 0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root] = 0;
for(int i = 0;i < n; ++i)
{
res += 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(int u = pre[v]; u != v;u = pre[u])
id[u] = tot;
id[v] = tot++;
}
}
if(tot == 0) break;
for(int i = 0;i < n; ++i)
if(id[i] == -1) id[i] = tot++;
for(int i = 0;i < m; ++i)
{
int u = t[i].x;
int v = t[i].y;
t[i].y = id[v];
t[i].x = id[u];
if(t[i].x != t[i].y) t[i].z -= in[v];
}
n = tot;
root = id[root];
}
return res;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
sum = 0;
for(int i = 0;i < m; ++i)
{
scanf("%d%d%d",&x,&y,&z);
t[i].x = x;
t[i].y = y;
t[i].z = z;
sum += z;
}
sum++;
for(int i = 0;i < n; ++i)
{
t[m].x = n;
t[m].y = i;
t[m++].z = sum;
}
ans = zhuliu(n,n + 1);
minr -= (m - n);
if(ans == -1 || ans >= 2 * sum) printf("impossible\n\n");
else printf("%d %d\n\n",ans - sum,minr);
}
return 0;
}
定根与不定根的思考
情况1: 图不需要自己额外建(即题目给好了N个点,M条边),只是没有给定根.
解:添加一个点作为根,向每个节点都连接一条边,而边的权值需要比其他所有边的权值加起来更大,表明这些点都有可能是最小树形图的根, (至于为什么权值应该是所有边权值加起来,个人是这样认为的:首先,权值不能为0,权值为0的话最小树形图就始终会是全部取与虚根相连的边构建成的最小树形图,而当权值取其他所有之和时,可以更加方便的判断是否能生成最小树形图),在得出答案后减去虚根相连边的权值即是答案.
要做的题
CodeForces - 240E UVA - 11865 HYSBZ - 4349 HDU - 4966 HDU - 4009 HDU - 2121