您的位置:首页 > 其它

最小树形图/朱刘算法……表示稍微记录一下

2016-03-12 23:01 267 查看
大概就是那么几步……呃…

1) 每个点找到权值最小的入边,记录为In[i];

2) 【假装是一只定根的图】判有无除了根节点之外的独立节点,如果有的话那这个图必然不连通 于是直接return -1

3) 然后开始找环,至于在这种有n条边的图上找环的事,参考noip2015提高组D1T2

4) 找环的时候顺手把环缩了(标记一下,创建新的节点)

5) 然后更新 新图 的每个节点之间的距离,于是就会出现下面这种鬼畜的东西↓↓↓

说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边
in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w'(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。                           ——摘自百度百科 最小树形图(看代码看不懂的那一段

关于为什么 在给点重新编号的时候,计算每个新的点之间的距离 是这样奇怪的公式

假设是点i到点集vj(也就是新点vj)的距离,vj由n个点组成(vj1,vj2,vj3.....vjn)

dis[i][vj]=min{dis[i][vjk]-in[vik]}(1<=k<=n)

但是点集vj到点i的距离却是

dis[vj][i]=min{dis[vjk][i]}(1<=k<=n)

相当于是每条边都减去它指向的那个点的最小入边长度(如果这条边两边的点不在同一个集合中的话)

6) 最后,默默表示,细节什么的,真的,很重要 orzorzorz,用左闭右开区间,【左闭右闭WA到半夜一点的某表示……对自己的信仰产生了怀疑

最后附上呆马,虽然丑哭了,也许还会又WA又T又RE

题目在……呃……这里的learn那道题

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std; int n,m;
int S=0;
const int INF=0x3f3f3f3f;

int aim[55];
int num[55],cnt_node=1;
struct t1{
int frm,to,nxt,lth;
}edge[10057]; int cnt_edge=0;
int fst[557];
void addedge(int x,int y,int z){
edge[++cnt_edge].to=y;
edge[cnt_edge].frm=x;
edge[cnt_edge].nxt=fst[x];
edge[cnt_edge].lth=z;
fst[x]=cnt_edge;
}

void init(){
for(int i=1;i<=n;++i){
for(int j=num[i]+1;j<num[i+1];++j)
addedge(j,j-1,0);
addedge(num[n+1],num[i],0);
}
while(m--){
int a,b,c,d,e;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
addedge(num[a]+b,num[c]+d,e);
}
}
//========================================================================
int ans=0;
int col[557];
int cnt_col;

int pre[557];
int In[557];
int vis[557];

int zhuliu(int root,int NE,int NV){
for(;;){
memset(In,INF,sizeof(In));
memset(pre,-1,sizeof(pre));

//初始化每个点的In
for(int i=1;i<=NE;++i)
if(edge[i].lth<In[edge[i].to]&&edge[i].to!=edge[i].frm)
In[edge[i].to]=edge[i].lth,pre[edge[i].to]=edge[i].frm;

//判有没有独立节点
for(int i=0;i<NV;++i){
if(i==root) continue;
if(In[i]==INF) return -1;
}

//判环,缩环
cnt_col=0;
memset(vis,-1,sizeof(vis));
memset(col,-1,sizeof(col));
In[root]=0;
for(int i=0;i<NV;++i){
ans+=In[i];
int now=i;
while(vis[now]!=i&&col[now]==-1&&now!=root){
vis[now]=i;
now=pre[now];
}
if(now!=root&&!(~col[now])){
for(int tmp=pre[now];tmp!=now;tmp=pre[tmp]) col[tmp]=cnt_col;
col[now]=cnt_col++;
}
}

//如果没有环就表示,树建好啦
if(!cnt_col) return ans;
//给节点标新序号
for(int i=0;i<NV;i++)
if(!(~col[i])) col[i]=cnt_col++;
//按照 入边长度-=In[i] 的规则更新每个新节点之间的距离
for(int i=1;i<=NE;++i){
int tt=edge[i].to;
edge[i].frm=col[edge[i].frm];
edge[i].to=col[edge[i].to];
if(edge[i].frm!=edge[i].to)
edge[i].lth-=In[tt];
}
NV=cnt_col;
root=col[root];
}
}

int main(){
memset(fst,0,sizeof(fst));
memset(edge,0,sizeof(edge));
num[0]=0;

while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){
for(int i=1;i<=n;++i) scanf("%d",&aim[i]),++aim[i],num[i+1]=num[i]+aim[i];
init();
printf("%d\n",zhuliu(num[n+1],cnt_edge,num[n+1]+1));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: