整理的算法模板合集: ACM模板
给定一个根的有向图的最小树形图
给定包含 n 个结点,m 条有向边的一个图。试求一棵以结点 r 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 r
为根的最小树形图,输出 −1。
#include<bits/stdc++.h>
using namespace std;
const int N=106,M=10006,inf=2e8;
int n,m,rt,t,cnt=0,id[N],pre[N],ine[N],vis[N];
struct line{
int x,y,c;}q[M];
inline int read(){
int T=0,F=1; char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') F=-1; ch=getchar();}
while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
return F*T;
}
int zhuliu(){
int ans=0;
while(true){
cnt=0;
for(int i=1;i<=n;++i) ine[i]=inf,vis[i]=0,id[i]=0;//预处理
for(int i=1;i<=m;++i) if(q[i].x!=q[i].y&&ine[q[i].y]>q[i].c) ine[q[i].y]=q[i].c,pre[q[i].y]=q[i].x;//每个点的最短边
for(int i=1;i<=n;++i) if(i!=rt&&ine[i]==inf) return -1;//有点无最短边
for(int i=1;i<=n;++i){
if(i==rt) continue;
ans+=ine[i],t=i;
while(vis[t]!=i&&!id[t]&&t!=rt) vis[t]=i,t=pre[t];
//能走到环的点或者换上的点停下
if(!id[t]&&t!=rt){
id[t]=++cnt; //将环上的点标记为新的环
for(int o=pre[t];o!=t;o=pre[o]) id[o]=cnt;
}
}//找环
if(!cnt) break;//无环结束
for(int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
for(int i=1;i<=m;++i){
t=q[i].y,q[i].x=id[q[i].x],q[i].y=id[q[i].y];
if(q[i].x!=q[i].y) q[i].c-=ine[t];
}
n=cnt,rt=id[rt];
//去旧图,换新图
}
return ans;
}
int main(){
n=read(),m=read(),rt=read();
for(int i=1;i<=m;++i) q[i].x=read(),q[i].y=read(),q[i].c=read();
printf("%d\n",zl());
return 0;
}
为给定根的树形图
求没有确定的根的树形图:建立一个超级根r,以它为根跑算法,只要将r向原图每个点连接一条权值大于原图中所有边的边权的边,这样选这些边肯定不划算,因此只会选择一条。
判断无解的方法
判无解的奇技淫巧:从小到大依次枚举每个点i,加入边 ( i , ( i + 1 ) % n + 1 , + ∞ ) (i,(i+1)\%n+1,+\infty) (i,(i+1)%n+1,+∞) ,这样如果你最后得到的答案为 + ∞ +\infty +∞,那么就无解了。