最小树形图
最小树形图实际上可以理解为有向图的最小生成树,它满足这样的要求:
1、不存在环;
2、除根结点的入度为0,其他点的入度都为1;
3、满足1和2的条件的所有子图中权值和最小;
朱刘算法的基本思想
每次找最小入边集,如果有环或不连通,环缩点重新建图再继续重复;
0、初始化:每个点入边最小权值初始化为极大值;
1、找最小入边集:就是找到每一个点所有入边里权值最小的那个,并记录下权值和前驱点;
2、找非根无入边点:如果能找到说明这个图不存在最小树形图;
3、找环 缩点:每个点从前驱点向前找,要么找到根,要么找到一个环,如果找到一个环,那么把环缩成一个点,更新节点编号;
4、重新构图:依据当前的图和缩点后的点建立新的图,重复上述步骤直到最终无环;
重新建图每条边的权值如何确定?每个点其余入边的权值减去最小入边的权值即可,因为我们只要权值和最小;
举个例子
第一次找到最小入边集找到(3,4)和(4,3),显然是一个环,这两条边要舍弃一条,按照上面说的:
3,4缩点带来的代价是2+4=6
对于3:(1,3)变为5-2=3,那么1->3->4的权值就是 6+3=9=5+4;
对于4:(2,4)变为6-4=2,那么2->4->3的权值就是 6+2=8=2+6;
把缩点中的多余代价在其他入边减掉,就相当于没有选这条边;
例题
题意:n个点建水井的代价是x*高度,从别家借水的代价是y*两点的曼哈顿距离,如果比供水点的高度高,要再加z;
分析:0点向所有点建x*高度的边;供水点向借水点按题意建边;
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1007;
const int maxm=1e6+7;
const int mod=1e9+7;
const int INF=0x7f7f7f7f;
int n,X,Y,Z,m;
struct point{int x,y,z;} p[maxn];
struct edge{int u,v,w;} g[maxm];
int pre[maxn],id[maxn],vis[maxn],in[maxn];
int cal(int i,int j)
{
if(i==j) return INF;
int tmp=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y)+abs(p[i].z-p[j].z);
if(p[i].z<p[j].z) return tmp*Y+Z;
return tmp*Y;
}
ll Directed_MST(int rt,int V,int E)
{
ll res=0;
while(1)
{
//0、初始化
for(int i=0;i<V;i++) in[i]=INF;
//1、找最小入边集
for(int i=0;i<E;i++)
{
int u=g[i].u,v=g[i].v;
if(g[i].w<in[v] && u!=v) {pre[v]=u;in[v]=g[i].w;}
}
//2、找非根无入边点(这题不需要)
for(int i=0;i<V;i++)
{
if(i==rt) continue;
if(in[i]==INF) return -1;
}
//3、找环 缩点(注意这里的V和id!!)
int cnt=0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[rt]=0;
for(int i=0;i<V;i++)
{
res+=1ll*in[i];int v=i;
while(vis[v]!=i && id[v]==-1 && v!=rt) {vis[v]=i;v=pre[v];}
//每个点寻找其前驱点,要么最后到根,要么找到一个环
if(v!=rt && id[v]==-1)
{
for(int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
id[v]=cnt++;
}//缩点
}
if(cnt==0) return res;
//4、重新构图
for(int i=0;i<V;i++) if(id[i]==-1) id[i]=cnt++;
for(int i=0;i<E;i++)
{
int u=g[i].u,v=g[i].v,w=g[i].w;
if(id[u]!=id[v]) w-=in[v];
g[i]={id[u],id[v],w};
}
V=cnt;rt=id[rt];
}
return res;
}
int main()
{
while(~scanf("%d%d%d%d",&n,&X,&Y,&Z))
{
if(!n && !X && !Y && !Z) break;
m=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
g[m++]={0,i,X*p[i].z};
}
for(int i=1;i<=n;i++)
{
int k;scanf("%d",&k);
while(k--)
{
int j;scanf("%d",&j);
g[m++]={i,j,cal(i,j)};
}
}
ll ans=Directed_MST(0,n+1,m);
if(ans==-1) printf("poor XiaoA\n");
printf("%lld\n",ans);
}
return 0;
}