20190716NOIP模拟赛T2 通讯(tarjan缩点+贪心)

题目描述

“这一切都是命运石之门的选择。” 

试图研制时间机器的机关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求强联通分量缩点,然后思路有点乱,想过一个个枚举边然后找min值(这就是正解),但觉得这种做法太傻逼,也就没打,又去看了一遍题面,发现有这么一句话“传达到所有分部”,然后就觉得是要经过所有点,就自然而然的想到了kruscal求最小生成树,但我忽略了最小生成树只是针对于无向图而言的,并不适用于有向图,qj测试点还没输出换行然后就爆零滚粗了。

题解

其实这题确实水,tarjan缩完点后就枚举每一个点然后找权值最小的入边就可以了,只要建个反图就很好处理。

其实是个贪心的思想,至于正确性的话,就是因为题目中保证都能到达,所以找最小入边的正确性也就很显然了,也可以自己话个图理解一下。

最后,对于多测题,数组不清空,爆零两行泪。

收获

还是基础知识不牢固,都不知道kruscal只适用于无向图,以后一定要多注意算法的适用范围这种东西,不然很容易爆零的。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #include<vector>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 const int N=5e5+10;
 9 int n,m,num,top;
10 int tot,first[N],nex[N],edge[N],to[N],s[N],in[N],c[N],cnt,low[N],dfn[N];
11 inline int read(){
12     int ss=0;char bb=getchar();
13     while(bb<48||bb>57)bb=getchar();
14     while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
15     return ss;
16 }
17 void add(int a,int b,int ci){
18     to[++tot]=b,edge[tot]=ci,nex[tot]=first[a],first[a]=tot;
19 }
20 int totc,firstc[N],toc[N],nexc[N],edgec[N];
21 void add_c(int a,int b,int ci){
22     toc[++totc]=b,edgec[totc]=ci,nexc[totc]=firstc[a],firstc[a]=totc;
23 }
24 void init(){
25     tot=0;
26     memset(first,0,sizeof(first));
27     memset(nex,0,sizeof(nex));
28     memset(to,0,sizeof(to));
29     memset(s,0,sizeof(s));
30     memset(in,0,sizeof(in));
31     memset(dfn,0,sizeof(dfn));
32     memset(low,0,sizeof(low));
33     memset(c,0,sizeof(c));
34     memset(firstc,0,sizeof(firstc));
35     memset(nexc,0,sizeof(nexc));
36     memset(toc,0,sizeof(toc));
37     memset(edgec,0,sizeof(edgec));
38     top=0,totc=0,cnt=0;
39 }
40 void tarjan(int x){
41     dfn[x]=low[x]=++num;s[++top]=x,in[x]=1;
42     for(int i=first[x];i;i=nex[i]){
43         int y=to[i];
44         if(!dfn[y]){
45             tarjan(y);
46             low[x]=min(low[y],low[x]);
47         }
48         else if(in[y]){
49             low[x]=min(low[x],dfn[y]);
50         }
51     }
52     if(dfn[x]==low[x]){
53         cnt++;int y;
54         do{
55             y=s[top--],in[y]=0;
56             c[y]=cnt;
57         }while(x!=y);
58     }
59 }
60 int main(){
61     for(;;){
62         n=read();m=read();
63         if(n==0&&m==0) break;
64         int pf=0;
65         init();
66         for(int i=1;i<=m;i++){
67             int a,b,ci;
68             a=read(),b=read(),ci=read();
69             a++,b++;pf+=ci;
70             add(a,b,ci);
71         }
72         if(m==n-1){
73             printf("%d\n",pf);
74             continue;
75         }
76         for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
77         for(int x=1;x<=n;x++){
78             for(int i=first[x];i;i=nex[i]){
79                 int y=to[i];
80                 if(c[x]==c[y]) continue;
81                 //cout<<c[x]<<" "<<c[y]<<" "<<edge[i]<<endl; 
82                 add_c(c[y],c[x],edge[i]);
83             }
84         }
85         //for(int i=1;i<=totc;i++) cout<<toc[i]<<" "<<nexc[i]<<" "<<edgec[i]<<endl;
86         long long ans=0;
87         for(register int x=1;x<=cnt;x++){
88             int res=1e9+10;
89             for(register int i=firstc[x];i;i=nexc[i]){
90                 //cout<<toc[i]<<" ";
91                 //cout<<edgec[i]<<endl;
92                 res=min(res,edgec[i]);
93             }
94             ans+=res;
95         }ans-=(1e9+10);
96         printf("%lld\n",ans);
97     }
98     return 0;
99 }
View Code

 

转载于:https://www.cnblogs.com/leom10/p/11195676.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值