模拟考试:图论专场
Posted fujudge
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟考试:图论专场相关的知识,希望对你有一定的参考价值。
Phone
【问题描述】
由于地震使得连接汶川县城电话线全部损坏,假如你是负责将电话线接到震中汶川县城的负责人,汶川县城周围分布着N(1 <= N <= 1,000)根按1..N顺次编号的废弃的电话线杆,任意两根电话线杆间都没有电话线相连。一共P(1 <= P <= 10,000)对电话线杆间可以拉电话线,其余的那些由于地震使得电线杆已经倒塌而无法被连接。
第i对电话线杆的两个端点分别为Ai、Bi,它们间的距离为Li (1 <= Li <= 1,000,000)。数据中保证每对(Ai,Bi)最多只出现1次。编号为1的电话线杆已经接入了全国的电话网络,整个县城的电话线全都连到了编号为N的电话线杆上。也就是说,你的任务仅仅是找一条将1号和N号电话线杆连起来的路径,其余的电话线杆并不一定要连入电话网络。
电信公司决定支援灾区免费为汶川县城连结K(0<=K<N)对由你指定的电话线杆。对于此外的那些电话线,需要为它们付费,等于其中最长的电话线的长度(每根电话线仅连结一对电话线杆)。如果需要连结的电话线杆不超过K对,那么总支出为0。
请你计算一下,将电话线引到震中汶川县城最少需要在电话线上花多少钱。
【Input】
输入文件的第一行包含三个用空格隔开的整数:N,P和K
第二行到第P+1行:,每行分别都为三个用空格隔开的整数:Ai,Bi和Li
【output】
输出文件中仅包含一个整数,表示在这项工程上的最小支出。如果任务不可能完成,则输出-1。
【Sample Input】
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
【Sample Output】
4
题解
一开始想写分层图,可是看到k<=1000就被吓尿了...果断写了二分答案,不过据说有队友分层图写AC了的...exm???总之两种都可以写
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 #define maxn 1005 6 #define inf 1<<29 7 using namespace std; 8 int n,p,k; 9 int d[maxn],vis[maxn],map[maxn][maxn],tmp[maxn][maxn]; 10 void spfa1() 11 { 12 queue<int> q; 13 for(int i=1 ; i<=n ; ++i) 14 { 15 d[i]=inf; 16 vis[i]=0; 17 } 18 vis[1]=1; 19 d[1]=0; 20 q.push(1); 21 while(!q.empty()) 22 { 23 int dd=q.front();q.pop(); 24 vis[dd]=0; 25 for(int i=1 ; i<=n ; ++i ) 26 if(map[dd][i]) 27 if(d[dd]+1<d[i]) 28 { 29 d[i]=d[dd]+1; 30 if(!vis[i]) 31 { 32 vis[i]=1; 33 q.push(i); 34 } 35 } 36 } 37 } 38 void spfa() 39 { 40 queue<int> q; 41 for(int i=1 ; i<=n ; ++i) 42 { 43 d[i]=inf; 44 vis[i]=0; 45 } 46 vis[1]=1; 47 d[1]=0; 48 q.push(1); 49 while(!q.empty()) 50 { 51 int dd=q.front();q.pop(); 52 vis[dd]=0; 53 for(int i=1 ; i<=n ; ++i ) 54 if(map[dd][i]) 55 if(d[dd]+tmp[dd][i]<d[i]) 56 { 57 d[i]=d[dd]+tmp[dd][i]; 58 if(!vis[i]) 59 { 60 vis[i]=1; 61 q.push(i); 62 } 63 } 64 } 65 } 66 int cal(int x) 67 { 68 for(int i=1 ; i<=n ; ++i) 69 for(int j=i ; j<=n ; ++j) 70 if(map[i][j]>x)tmp[i][j]=tmp[j][i]=1; 71 else tmp[i][j]=tmp[j][i]=0; 72 spfa(); 73 return d[n]; 74 } 75 int main() 76 { 77 freopen("phone.in","r",stdin); 78 freopen("phone.out","w",stdout); 79 int u,v,w; 80 scanf("%d%d%d",&n,&p,&k); 81 for(int i=1 ; i<=p ; ++i ) 82 { 83 scanf("%d%d%d",&u,&v,&w); 84 map[u][v]=map[v][u]=w; 85 } 86 spfa1(); 87 if(d[n]<=k) 88 { 89 printf("0"); 90 return 0; 91 } 92 int l=1,r=1000000; 93 while(r>l) 94 { 95 int m=(l+r)>>1; 96 if(cal(m)>k)l=m+1; 97 else r=m; 98 } 99 if(r==1000000)printf("-1"); 100 else printf("%d",r); 101 return 0; 102 }
Road(Road)
【问题描述】
有N个村庄编号从1~N。小G要建一些路,使每两个村庄都连通。连通的定义为:如果A和B直接相连,则A&B连通。如果A&C连通,B&C连通,则A&B连通。小G发现有些村庄间已经有路,小G想知道修路的最小总长度是多少。
【Input】
第一行一个正整数N,表示村庄的数量。
接下来N行每行N个数,第i行第j个数表示从i到j的距离D。
接下来一个正整数M,表示已经存在的边的数量。
接来下M行每行两个正整数,表示已经存在的边连接的两个村庄的编号。
【output】
一个整数,表示使所有村庄两两相连的最短修路距离。
【Sample Input】
3
0 990 692
990 0 179
692 179 0
1
1 2
【Sample Output】
179
【数据范围】
3<=N<=100
1<=D<=1000
0<=M<=N*(N+1)/2
题解
裸的kruskal,这里提供另外一种写法,Floyd传递闭包,考试的时候写炸了mmp,和傻逼一样的自己...
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 #define maxn 105 7 int link[maxn][maxn],map[maxn][maxn]; 8 int ans,n,m,sum,cnt; 9 void floyd() 10 { 11 for(int k=1 ; k<=n ; ++k) 12 for(int i=1 ; i<=n ; ++i) 13 if(link[i][k]) 14 for(int j=i+1 ; j<=n ; ++j) 15 if(link[k][j])link[i][j]=link[j][i]=1; 16 } 17 struct edge{ 18 int u,v,w; 19 }E[maxn*maxn]; 20 void add(int u,int v) 21 { 22 E[++cnt].u=u; 23 E[cnt].v=v; 24 E[cnt].w=map[u][v]; 25 } 26 bool cmp(edge x,edge y){return x.w<y.w;} 27 int main() 28 { 29 // freopen("road.in","r",stdin); 30 // freopen("road.out","w",stdout); 31 int u,v; 32 scanf("%d",&n); 33 for(int i=1 ; i<=n ; ++i ) 34 for(int j=1 ; j<=n ; ++j ) 35 scanf("%d",&map[i][j]); 36 scanf("%d",&m); 37 for(int i=1 ; i<=n ; ++i)link[i][i]=1; 38 for(int i=1 ; i<=m ; ++i) 39 { 40 scanf("%d%d",&u,&v); 41 link[u][v]=link[v][u]=1; 42 } 43 floyd(); 44 for(int i=1 ; i<=n ; ++i) 45 for(int j=i+1 ; j<=n ; ++j) 46 if(link[i][j]==0)add(i,j); 47 sort(E+1,E+1+cnt,cmp); 48 for(int i=1 ; i<=cnt ; ++i ) 49 { 50 int u=E[i].u,v=E[i].v; 51 if(link[u][v])continue; 52 int w=E[i].w; 53 sum+=w; 54 link[u][v]=link[v][u]=1; 55 for(int k=1 ; k<=n ; ++k ) 56 if(link[u][k]) 57 for(int p=1 ; p<=n ; ++p) 58 if(link[v][p])link[k][p]=link[p][k]=1; 59 } 60 printf("%d",sum); 61 return 0; 62 }
Sightseeing
【问题描述】
小G计划在暑假去北京旅游。她已经知道了n个景点和景点间交通的路费,并确定好了起点和终点。为了节省路费,小G当然会选择从起点到终点的最短路。两条路径上只要有一条边不同这两条路径就算作不同的路径(无重边)。她通过观察发现最短路的数量太少,这令她非常不爽,于是她决定比最短路长度大1的方案也可以采用。
【Input】
第一行为数据组数T,T不会超过5。
第二行两个正整数N&M,表示景点个数和边的数量。
接下来M行,每行三个整数A,B,L,表示从A到B的路径长为L(单向的)。
接下来一行为起点和终点。
【output】
共一行,表示最短路的数量+比最短路长1的路径数量。答案不会超过 109。
【Sample Input】
2
4 10
3 2 1
1 3 1
3 1 1
3 1 1
2 4 1
1 2 1
4 1 1
3 2 1
4 1 1
3 1 1
3 2
4 10
3 4 2
3 4 2
3 1 1
4 2 1
1 3 1
1 2 3
3 4 2
4 3 1
4 3 3
3 4 3
3 4
【Sample Output】
5
4
【数据范围】
2<=N<=1000
1<=M<=10000
题解
一遍spfa找最短路,然后dfs寻找路径...
考试的时候很坚定的认为dfs过不了,10^9的数据啊...结果最大数据也只是100过一点
考试写了两个spfa,第二个记录每一个点达到最短路及最短路+1情况的方案数。因为判断+1成立的条件是dis[v]=dis[d]+w-1,因为题目写着单向边就很大胆的写了,结果这尼玛把一对双向边当成两个单向边输入了???what the fuck??全死循环了(边权为1的边会造成死循环,请自行思考)没脾气了,暴力出奇迹,暴力出奇迹...
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 #define maxn 1005 6 #define maxm 10005 7 #define inf 1<<29 8 using namespace std; 9 int n,m,st,ed,ecnt; 10 int dis[maxn],vis[maxn],cnt[maxn][3],head[maxn],ro[maxn][maxn]; 11 struct edge{ 12 int u,v,w,next; 13 }E[maxm]; 14 void addedge(int u,int v,int w) 15 { 16 E[++ecnt].u=u; 17 E[ecnt].v=v; 18 E[ecnt].w=w; 19 E[ecnt].next=head[u]; 20 head[u]=ecnt; 21 } 22 void spfa() 23 { 24 queue<int> q; 25 for(int i=1 ; i<=n ; ++i) 26 { 27 vis[i]=0; 28 dis[i]=inf; 29 } 30 dis[st]=0;vis[st]=1; 31 q.push(st); 32 while(!q.empty()) 33 { 34 int dd=q.front();q.pop(); 35 vis[dd]=0; 36 for(int i=head[dd] ; i ; i=E[i].next ) 37 { 38 int v=E[i].v; 39 int w=E[i].w; 40 if(dis[v]>=dis[dd]+w) 41 { 42 dis[v]=dis[dd]+w; 43 if(!vis[v]) 44 { 45 vis[v]=1; 46 q.push(v); 47 } 48 } 49 } 50 } 51 } 52 int dfs(int u,int sum) 53 { 54 int ret(0); 55 if(sum>dis[ed]+1)return 0; 56 if(u==ed&&(sum==dis[ed]||sum==dis[ed]+1))return 1; 57 for(int i=head[u] ; i ; i=E[i].next ) 58 { 59 int v=E[i].v; 60 int w=E[i].w; 61 if(vis[v])continue; 62 vis[v]=1; 63 ret+=dfs(v,sum+w); 64 vis[v]=0; 65 } 66 return ret; 67 } 68 int read() 69 { 70 int ret(0); 71 char ch; 72 ch=getchar(); 73 while(ch<‘0‘||ch>‘9‘)ch=getchar(); 74 while(ch>=‘0‘&&ch<=‘9‘) 75 { 76 ret=ret*10+ch-‘0‘; 77 ch=getchar(); 78 } 79 return ret; 80 } 81 void init() 82 { 83 for(int i=1 ; i<=n ; ++i) 84 head[i]=0; 85 ecnt=0; 86 } 87 int main() 88 { 89 freopen("sightseeing.in","r",stdin); 90 freopen("sightseeing.out","w",stdout); 91 int T,u,v,w; 92 scanf("%d",&T); 93 while(T--) 94 { 95 scanf("%d%d",&n,&m); 96 for(int i=1 ; i<=m ; ++i ) 97 { 98 u=read();v=read();w=read(); 99 addedge(u,v,w); 100 } 101 scanf("%d%d",&st,&ed); 102 spfa(); 103 for(int i=1 ; i<=n ; ++i )vis[i]=0; 104 vis[st]=1; 105 printf("%d\n",dfs(st,0)); 106 init(); 107 } 108 return 0; 109 }
以上是关于模拟考试:图论专场的主要内容,如果未能解决你的问题,请参考以下文章