B. 通讯
题目描述
“这一切都是命运石之门的选择。”
试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此得知了伦太郎制作出了电话微波炉(仮)。
为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯 网络,传达到所有分部。
SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线 路有一个固定的通讯花费Ci。
为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向 另一个与它有线路的部门传递(可能存在多条通信线路)。我们定义总费用为所 有部门传递消息的费用和。
幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方 法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费。
由于资金问题(预算都花在粒子对撞机上了),SERN总部的工程师希望知道, 达到目标的最小花费是多少。
输入格式
多组数据,文件以2个0结尾。
每组数据第一行,一个整数N,表示有N个包括总部的部门(从0开始编号)。 然后是一个整数M,表示有M条单向通讯线路。
接下来M行,每行三个整数,Xi,Yi,Ci,表示第i条线路从Xi连向Yi,花费为 Ci。
输出格式
每组数据一行,一个整数表示达到目标的最小花费。
样例
样例输入
3 3
0 1 100
1 2 50
0 2 100
3 3
0 1 100
1 2 50
2 1 100
2 2
0 1 50
0 1 100
0 0
样例输出
150
100
50
数据范围与提示
样例解释
第一组数据:总部把消息传给分部1,分部1再传给分部2.总费用:100+50=150.
第二组数据:总部把消息传给分部1,由于分部1和分部2可以互相传递消息,所以分部1可以无费用把消息传给2.总费用:100+0=100.
第三组数据:总部把消息传给分部1,最小费用为50.总费用:50.
数据范围
对于10%的数据,保证M=N-1
对于另30%的数据,N ≤ 20 ,M ≤ 20
对于100%的数据,N ≤ 50000 ,M ≤ 10^5 ,Ci ≤ 10^5 ,
数据组数 ≤ 5
数据保证一定可以将信息传递到所有部门。
水题
tarjan缩点,贪心找每个点的最小入边即可(我用dfs实现,本质都是贪心)
我的tarjan+dfs(下面有天皇skyh的贪心找小边)
#include<iostream> #include<cstdio> #include<vector> using namespace std; int n,m,dfn[51000],low[51000],num,st[51000],sp[51000],ins[51000],mi[51000],v[51000],cnt; struct node{ int ver,len; }; vector<node>to[51000],son[51000]; void tarjan(int x){ dfn[x]=low[x]=++num; st[++st[0]]=x; ins[x]=1; //sort(to[x].begin(),to[x].end(),cmp); for(int i=0;i<to[x].size();i++){ node w=to[x][i]; if(!dfn[w.ver]){ tarjan(w.ver); low[x]=min(low[x],low[w.ver]); } else if(ins[w.ver]) low[x]=min(low[x],dfn[w.ver]); } if(low[x]==dfn[x]){ cnt++; int y; do{ y=st[st[0]--]; ins[y]=0; sp[y]=cnt; }while(y!=x); } } long long dfs(int x){ v[x]=1; long long cet=0; //sort(son[x].begin(),son[x].end(),cmp); // cout<<x<<" "<<son[x].size()<<endl; for(int i=0;i<son[x].size();i++){ node w=son[x][i]; //cout<<w.ver<<" "<<w.len<<endl; if(v[w.ver]){ if(mi[w.ver]>w.len){ cet-=(mi[w.ver]-w.len); mi[w.ver]=w.len; } continue; } cet+=dfs(w.ver); cet+=w.len; mi[w.ver]=w.len; } ///cout<<cet<<endl; return cet; } int main(){ scanf("%d%d",&n,&m); while(n!=0||m!=0){ int x; for(int i=1;i<=m;i++){ node w; scanf("%d%d%d",&x,&w.ver,&w.len); w.ver++; to[x+1].push_back(w); } // for(int i=1;i<=n;i++) cout<<i<<" "<<to[i].size()<<endl; for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); //for(int i=1;i<=n;i++) cout<<i<<" "<<to[i].size()<<endl; for(int i=1;i<=n;i++){ //cout<<i<<" "<<sp[i]<<" "<<to[i].size()<<endl; for(int j=0;j<to[i].size();j++){ node w=to[i][j]; if(sp[i]!=sp[w.ver]){ w.ver=sp[w.ver]; son[sp[i]].push_back(w); } } } /*for(int i=1;i<=cnt;i++){ cout<<i<<" "<<son[i].size()<<endl; for(int j=0;j<son[i].size();j++){ printf("%d %d %d\n",i,son[i][j].ver,son[i][j].len); } }*/ long long ans=dfs(sp[1]); printf("%lld\n",ans); for(int i=1;i<=n;i++){ dfn[i]=low[i]=v[i]=sp[i]=mi[i]=v[i]=0; to[i].clear(); son[i].clear(); num=0; cnt=0; } scanf("%d%d",&n,&m); } }
——————————————————————————————————————————————————————————————————————————————
天皇skyh的贪心找小边
#include<iostream> #include<cstdio> #include<cstring> #define db(x) cerr<<#x<<"="<<x<<endl #define cl(x) memset(x,0,sizeof(x)) using namespace std; const int N=50100,M=501000; int n,m,scc; int tot,head[N],to[M],nxt[M],w[M]; int top,stack[N],dfn[N],low[N],num,bl[N]; bool instack[N]; int imin[N]; void tarjan(int x) { dfn[x]=low[x]=++num; stack[++top]=x; instack[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(instack[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { ++scc; int y; do{ y=stack[top--]; bl[y]=scc; instack[y]=0; }while(y!=x); } } int main() { while(scanf("%d%d",&n,&m)==2&&(n||m)) { cl(dfn); cl(head); memset(imin,0x3f,sizeof(imin)); tot=0; top=0; num=0; scc=0; for(int i=1,a,b,d;i<=m;i++) { scanf("%d%d%d",&a,&b,&d); a++; b++; to[++tot]=b; w[tot]=d; nxt[tot]=head[a]; head[a]=tot; } tarjan(1); //db(scc); for(int i=1;i<=n;i++) for(int j=head[i];j;j=nxt[j]) { int y=to[j]; if(bl[i]==bl[y]) continue; imin[bl[y]]=min(imin[bl[y]],w[j]); } long long ans=0; for(int i=1;i<=scc;i++) if(imin[i]<=10000000) ans+=imin[i]; printf("%lld\n",ans); } return 0; }