2014-7-7 NOIP模拟赛(图论)
1.无线通讯网(wireless.pas/cpp/c)
【题目描述】
国防部计划用无线网络连接若干个边防哨所。2种不同的通讯技术用来搭建无线网络;每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都拥有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过D,这是受收发器的功率限制。收发器的功率越高,通话距离D会更远,但同时价格也会更贵。
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个D。
你的任务是确定收发器必须的最小通话距离D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。
【输入格式】 wireless.in
第1行:2个整数S(1<=S<=100)和P(S<P<=500),S表示可安装的卫星电话的哨所数,P表示边防哨所的数量。
接下里P行,每行描述一个哨所的平面坐标(x,y),以km为单位,整数,0<=x,y<=10000。
【输出格式】 wireless.out
第1行:1个实数D,表示无线电收发器的最小传输距离。精确到小数点后两位。
【样例输入】
2 4
0 100
0 300
0 600
150 750
【样例输出】
212.13
数据范围
对于20%的数据 P=2,S=1
对于另外20%的数据 P=4,S=2
对于100%的数据 1<=S<=100,S<P<=500
/* 看到“至少有一条通话路径”我们就可以想到用生成树来解决 这个题的特点在于存在卫星电话,对他们来说不需要连边 也就是找出含有n-1条边的最小生成树的过程中,只需找到第n-m条即可 */ #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; #define maxn 304800 int n,m,fa[maxn],x[510],y[510],num; struct node{ int from,to; double v; }e[maxn]; double count(int a,int b){ double res; res=sqrt((long double)(x[a]-x[b])*(long double)(x[a]-x[b])+(long double)(y[a]-y[b])*(long double)(y[a]-y[b])); return res; } int cmp(node a,node b){ return a.v<b.v; } int find(int a){ if(fa[a]==a)return fa[a]; else return fa[a]=find(fa[a]); } bool connect(int a,int b){ int f1=find(a),f2=find(b); if(f1==f2)return 0; fa[f1]=f2;return 1; } int main(){ freopen("Cola.txt","r",stdin); scanf("%d%d",&m,&n); for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ e[++num].from=i; e[num].to=j; e[num].v=count(i,j); } } sort(e+1,e+num+1,cmp); int cnt=0; for(int i=1;i<=num;i++){ if(connect(e[i].from,e[i].to)){ cnt++; if(cnt==n-m) printf("%.2lf",e[i].v); } } }
2.混合图(dizzy.pas/cpp/c)
【题目描述】
Hzwer神犇最近又征服了一个国家,然后接下来却也遇见了一个难题。
Hzwer的国家有n个点,m条边,而作为国王,他十分喜欢游览自己的国家。他一般会从任意一个点出发,随便找边走,沿途欣赏路上的美景。但是我们的Hzwer是一个奇怪的人,他不喜欢走到自己以前走过的地方,他的国家本来有p1条有向边,p2条无向边,由于国王奇怪的爱好,他觉得整改所有无向边,使得他们变成有向边,要求整改完以后保证他的国家不可能出现从某个地点出发顺着路走一圈又回来的情况。(注:m=p1+p2.)
概述:给你一张混合图,要求你为无向图定向,使得图上没有环。
【输入格式】 dizzy.in
第一行3个整数 n,p1,p2,分别表示点数,有向边的数量,无向边的数量。
第二行起输入p1行,每行2个整数 a,b 表示a到b有一条有向边。
接下来输入p2行,每行2个整数 a,b 表示a和b中间有一条无向边。
【输出格式】 dizzy.out
对于每条无向边,我们要求按输入顺序输出你定向的结果,也就是如果你输出a b,那表示你将a和b中间的无向边定向为a->b。
注意,也许存在很多可行的解。你只要输出其中任意一个就好。
【样例输入】
4 2 3
1 2
4 3
1 3
4 2
3 2
【样例输出】
1 3
4 2
2 3
数据范围
对于20%的数据 n<=10 p1<=10 p2<=5
对于30%的数据 n<=10 p1<=30 p2<=20
对于100%的数据 n<=100000 p1<=100000 p2<=100000
数据保证至少有一种可行解。
/* 拓扑排序:图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。 再加入所有有向边之后,进行一次拓扑排序 以后只要有双向边要加入就让拓扑序中编号较小的一个连向编号较大的一个,就能保证无环 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<cmath> #define inf 0x7fffffff #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,p1,p2,cnt,top,tot; int head[100005],r[100005]; int q[100005],s[100005],h[100005]; struct data{int to,next;}e[100005]; void insert(int u,int v) {e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;} void topsort() { for(int i=1;i<=n;i++) if(!r[i]){s[++top]=i;q[++tot]=i;}//统计入度为0的点 while(top) { int x=s[top--]; for(int i=head[x];i;i=e[i].next) { r[e[i].to]--; if(!r[e[i].to]){s[++top]=e[i].to;q[++tot]=e[i].to;} } } } int main() { freopen("dizzy.in","r",stdin); freopen("dizzy.out","w",stdout); n=read();p1=read();p2=read(); for(int i=1;i<=p1;i++) { int u=read(),v=read(); insert(u,v); r[v]++;//入度 } topsort(); for(int i=1;i<=n;i++) h[q[i]]=i; for(int i=1;i<=p2;i++) { int u=read(),v=read(); if(h[u]>=h[v])printf("%d %d\n",v,u); else printf("%d %d\n",u,v); } return 0; }
3.小K的农场(farm.pas/cpp/c)
【题目描述】
小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:农场a比农场b至少多种植了c个单位的作物,农场a比农场b至多多种植了c个单位的作物,农场a与农场b种植的作物数一样多。但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
【输入格式】 farm.in
第一行包括两个整数n和m,分别表示农场数目和小K记忆中的信息数目。
接下来m行:
如果每行的第一个数是1,接下来有3个整数a,b,c,表示农场a比农场b至少多种植了c个单位的作物。
如果每行的第一个数是2,接下来有3个整数a,b,c,表示农场a比农场b至多多种植了c个单位的作物。
如果每行第一个数是3,家下来有2个整数a,b,表示农场a终止的数量和b一样多。
【输出格式】 farm.out
如果存在某种情况与小K的记忆吻合,输出“Yes”,否则输出“No”。
【样例输入】
3 3
3 1 2
1 1 3 1
2 2 3 2
【样例输出】
Yes
样例解释:三个农场种植数量可以为(2,2,1)。
对于100%的数据 1<=n,m,a,b,c<=10000.
逼我写了读入优化,然后又逼我把bfs的spfa换成了dfs,倪够了
那么以后判负环,用dfs,读整型,写qread。
另附dfs判负环
思路是dis设为0,枚举每个点u,如果d(u)+w<d(v)就搜v,如果搜到的节点曾搜到过说明找到了负环。
为什么是对的呢?对于一个负环,一定可以找到一个节点从这里开始走一直累加权值,权值一直为负。
#include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; #define maxn 100010 queue<int>q; int n,m,dis[maxn],num,head[maxn*2],step[maxn]; bool vis[maxn]; int qread(){ int i=0,j=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')j=-1;ch=getchar();} while(ch<='9'&&ch>='0'){i=i*10+ch-'0';ch=getchar();} return i*j; } struct node{ int to,pre,v; }e[maxn*2]; void Insert(int from,int to,int v){ e[++num].to=to; e[num].v=v; e[num].pre=head[from]; head[from]=num; } bool spfa(){ memset(dis,127/3,sizeof(dis)); vis[0]=1;q.push(0);dis[0]=0;step[0]=1; while(!q.empty()){ int point=q.front(); q.pop(); vis[point]=0; for(int i=head[point];i;i=e[i].pre){ int to=e[i].to; if(dis[to]>dis[point]+e[i].v){ dis[to]=dis[point]+e[i].v; if(!vis[to]){ vis[to]=1; step[to]++; q.push(to); if(step[to]>=n)return 1; } } } } return 0; } int main(){ freopen("Cola.txt","r",stdin); n=qread();m=qread(); int op,x,y,z; for(int i=1;i<=m;i++){ op=qread(); if(op==2){ x=qread();y=qread();z=qread(); Insert(y,x,z); } if(op==1){ x=qread();y=qread();z=qread(); Insert(x,y,-z); } if(op==3){ x=qread();y=qread(); Insert(x,y,0); Insert(y,x,0); } } for(int i=1;i<=n;i++)Insert(0,i,0); if(spfa())printf("No"); else printf("Yes"); return 0; }
/* 差分约束。 由题意可知三个不等式: b-a<=-c a-b<=c a=b 进而从a向b连一条-c的边,或者从b向a连一条权值为c的边, 或者a、b之间连一条权值为0的双向边; 设置零号点, 然后向每个点连一条权值为0的单向边。 最后判断有无负环即可。 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 100010 int n,m,dis[maxn],num,head[maxn*2],step[maxn]; bool vis[maxn],flag; int qread(){ int i=0,j=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')j=-1;ch=getchar();} while(ch<='9'&&ch>='0'){i=i*10+ch-'0';ch=getchar();} return i*j; } struct node{ int to,pre,v; }e[maxn*2]; void Insert(int from,int to,int v){ e[++num].to=to; e[num].v=v; e[num].pre=head[from]; head[from]=num; } void spfa(int x){ if(flag)return; vis[x]=1; for(int i=head[x];i;i=e[i].pre){ int to=e[i].to; if(dis[to]>dis[x]+e[i].v){ dis[to]=dis[x]+e[i].v; if(vis[to]){flag=1;return;} spfa(to); } } vis[x]=0; } int main(){ freopen("Cola.txt","r",stdin); n=qread();m=qread(); int op,x,y,z; for(int i=1;i<=m;i++){ op=qread(); if(op==1){ x=qread();y=qread();z=qread(); Insert(x,y,-z); } if(op==2){ x=qread();y=qread();z=qread(); Insert(y,x,z); } if(op==3){ x=qread();y=qread(); Insert(x,y,0); Insert(y,x,0); } } for(int i=1;i<=n;i++)Insert(0,i,0); memset(dis,127/3,sizeof(dis)); dis[0]=0; spfa(0); if(flag)printf("No");//存在负环 else printf("Yes"); return 0; }