首页 > 技术文章 > 7.3图论模拟

yanlifneg 2016-07-03 10:50 原文

FJSC图论测试

题目

 

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

 

 

/*
"你的任务是确定收发器必须的最小通话距离D,
使得每一对哨所之间至少有一条通话路径
(直接的或者间接的)。"
恩 这句话要好好理解 反正我已开始是理解错了
第一遍用floyed跑了跑   
D不是1-n的dis 而是1-n的路径上的所有边的最大值
这样的话 我们要求出可以连通所有点的一条路径
再用卫星电话删掉大的 
看上去很明显了 最小生成树 显然Kruskal更适合 
因为他就是按边的权值从小到大 借助并茶几 
这恰好利于我们计算卫星电话的个数 
下面是wa的代码 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 510
using namespace std;
int m,n,k,p;
bool f[maxn];
double x[maxn],y[maxn],g[maxn][maxn];
struct node
{
    double dis;
    int s,t;
}e[maxn*maxn];
int cmp(const node &a,const node &b)
{
    return a.dis>b.dis;
}
double Get_dis(int i,int j)
{
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
void floyed()
{
    for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
          g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
      cin>>x[i]>>y[i];
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++)
        g[i][j]=Get_dis(i,j);
    floyed();
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++)
        {
          e[++k].dis=g[i][j];
          e[k].s=i;e[k].t=j;
        }
    sort(e+1,e+1+k,cmp);p=1;
    for(int i=1;i<=n&&m;i++)
      {
          int si=e[i].s,ti=e[i].t;
          if(!f[si])f[si]=1,m--;
          if(m==0)break;
          if(!f[ti])f[ti]=1,m--,p=i;
      }
    printf("%.2f\n",e[p].dis);
    return 0;
}
/*这是Ac的代码*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 510
using namespace std;
int m,n,k,p,tot,fa[maxn];
bool f[maxn];
double x[maxn],y[maxn],g[maxn][maxn];
struct node
{
    double dis;
    int s,t;
}e[maxn*maxn];
int cmp(const node &a,const node &b)
{
    return a.dis<b.dis;
}
double Get_dis(int i,int j)
{
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int find(int x)
{
    if(x!=fa[x])fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    //freopen("wireless.in","r",stdin);
    //freopen("wireless.out","w",stdout);
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
      cin>>x[i]>>y[i],fa[i]=i;
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++)
        {
          e[++k].dis=Get_dis(i,j);
          e[k].s=i;e[k].t=j;
        }
    sort(e+1,e+1+k,cmp);
    for(int i=1;i<=k;i++)
      {
          int r1=find(e[i].s);
          int r2=find(e[i].t);
          if(r1!=r2)
            {
                tot++;fa[r2]=r1;
          }
        if(tot+m>=n)
          {
              p=i;break;
          }
      }
    printf("%.2f\n",e[p].dis);
    return 0;
}

 

 

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

数据保证至少有一种可行解。

 

 

/*
这题实在没啥办法了 - -
暴力的话也就得10分(2^n)
直接看了题解 但还是不明白 
先按又向边拓扑排序 然后按照拓扑序 
处理每一条无向边 方向是从拓扑序小的指向大的
(以下纯属扯淡 只是个人想法) 
其实最开始接触的拓扑排序就是用来判断有没有环的
有向无环图里的拓扑序总的趋势就是小的指向大的
如果我们确定无向边的方向的时候由大的指向小的
那显然就构造了一个环 既然题目保证有解 我们不妨这样连
每一条都避免成环 
(好吧说不下去了 如果有神犇明白了 请留言 ) 
*/
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
#define maxn 200010
using namespace std;
int n,p1,p2,head[maxn],num,q[maxn],k,ru[maxn],order[maxn];
stack<int>s;
struct node
{
    int u,v,pre;
}e[maxn];
void Add(int from,int to)
{
    num++;e[num].u=from;e[num].v=to;
    e[num].pre=head[from];head[from]=num;
}
void topsort()
{
    for(int i=1;i<=n;i++)
      if(ru[i]==0)s.push(i),q[++k]=i;
    while(!s.empty())
      {
          int p=s.top();s.pop();
          for(int i=head[p];i;i=e[i].pre)
            {
                ru[e[i].v]--;
                if(ru[e[i].v]==0)q[++k]=e[i].v,s.push(e[i].v);
          }
      }
}
int main()
{
    //freopen("dizzy.in","r",stdin);
    //freopen("dizzy.out","w",stdout);
    scanf("%d%d%d",&n,&p1,&p2);
    int u,v;
    for(int i=1;i<=p1;i++)
      {
          scanf("%d%d",&u,&v);
          Add(u,v);ru[v]++;
      }
    topsort();
    for(int i=1;i<=n;i++)
      order[q[i]]=i;
    for(int i=1;i<=p2;i++)
      {
          scanf("%d%d",&u,&v);
          if(order[u]>order[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.

 

/*
这题一看就是查分约束 然而我似乎还是不理解这个东西
自己敲了一下发现不对 算了 先慢慢理解一下这个东西吧
注意 下面的代码是Wa的 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 10010
using namespace std;
int n,m,head[maxn],num,dis[maxn];
struct node
{
    int u,v,t,pre;
}e[maxn];
bool f[maxn];
queue<int>q;
void Add(int from,int to,int dis)
{
    num++;e[num].u=from;e[num].v=to;e[num].t=dis;
    e[num].pre=head[from];head[from]=num;
}
void SPFA()
{
    memset(dis,128,sizeof(dis));
    f[1]=1;q.push(1);dis[1]=0;
    while(!q.empty())
      {
          int k=q.front();q.pop();
          for(int i=head[k];i;i=e[i].pre)
            {
                dis[e[i].v]=max(dis[e[i].v],dis[k]+e[i].t);
                if(f[e[i].v]==0)
                  {
                      f[e[i].v]=1;
                      q.push(e[i].v);
              }
          }
      }
}
int main()
{
    freopen("farm.in","r",stdin);
    freopen("farm.out","w",stdout);
    scanf("%d%d",&n,&m);
    int a,x,y,z;
    for(int i=1;i<=m;i++)
      {
          scanf("%d",&a);
          if(a==1)scanf("%d%d%d",&x,&y,&z),Add(x,y,z);
          if(a==2)scanf("%d%d%d",&x,&y,&z),Add(y,x,-z);
          if(a==3)scanf("%d%d",&x,&y),Add(x,y,0);
      }
    SPFA();
    if(dis[n]>=0)printf("Yes\n");
    else printf("No\n");
}

 

推荐阅读