ccf 201403-4 无线网络 (广搜解法)

链接:http://115.28.138.223/view.page?gpid=T7

 

 

问题描述

  目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
  除此以外,另有 m 个可以摆放无线路由器的位置。你可以在这些位置中选择至多 k 个增设新的路由器。
  你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。请问在最优方案下中转路由器的最少个数是多少?

输入格式

  第一行包含四个正整数 n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
  接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线路由器在 (xi, yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
  接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi, yi) 点处可以增设 一个路由器。
  输入中所有的坐标的绝对值不超过 108,保证输入中的坐标各不相同。

输出格式

  输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路由器到第 2 个路由器最少经过的中转路由器的个数。

样例输入

5 3 1 3
0 0
5 5
0 3
0 5
3 5
3 3
4 4
3 0

样例输出

2

 

 

分析:这题是要进行搜索,在运动到一个本来没有,但可以设置路由器的地方的时候,就说明这次搜索在那个地方安置了路由器,这让我感到每条路径会影响整张图(设置路由器),所以先试了试深搜,但是数据不水,深搜超时了。

转而使用广搜,但由于广搜在某一层的各个结点时不知道当前路径已经访问过哪些路由器,尚未访问过哪些路由器,所以在每个结点内都保存一个visit数组,它首先继承其父亲的visit数组,然后将在进入该结点时所访问的路由器i的visit[i]设置为1,这样就设置好了。以上是广搜的遍历算法。但这还不够,仍然会超时。需要使用下面的性质:

 

将已有的路由器记为A0,A1,A2,...,An。将待放置的路由器记为B0,B1,B2,...Bm。

假设从根节点开始,广搜进行到了Ai,此时的路径为:

A0,...,Bj,...Bk,...Ai

那么就可以确定,其他的广搜方法

A0,...Bm,...Bn,...Ai不需要再遍历,证明如下:

假如最短路径经过Ai,完整路径如下:

A0,...,Bj,...Bk,...Ai,...Bp,...Bq,...A1

可以证明Ai到A1路径上所经过的每个B,必然均是首次遇到,否则,说明搜索的路中出现了环,不是最短路。

因此,A0,...,Bj,...Bk,...Ai和A0,...Bm,...Bn,...Ai这两者事实上是等价的,因为这两条路径在Ai前都是用了两个B,而Ai之后的也相同,故最终所使用的中间路由器的数量相同。所以可以不要再遍历A0,...Bm,...Bn,...Ai。

 

根据这个思路,可以进行剪枝。
设置long long visit_node_k[MAX_INDEX][MAX_INDEX];数组(也可以定义为int类型)

初始时将此数组所有元素置为0。

当广度优先搜索到结点i,并且当前已经使用了out.alreadset个B时,若visit_node_k[i][out.alreadyset] == 1,则说明之前已经处理过了类似的,可以剪枝,不再广搜这个结点。否则将visit_node_k[i][out.alreadyset] = 1;后,将该结点入队列(处理该节点)。

 

注意:这题如果不采用上诉策略进行剪枝,广搜也会超时。

 

还有一点,这里的距离和坐标应该使用long long类型,否则会溢出。

有一点忘记了,long long类型scanf时应该时%lld,写错了%lld导致只给90分,改过来之后才ac100

 

ac代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#define INF 123123123
#define MAX_INDEX 204
using namespace std;


bool reach(long long x1,long long y1,long long x2,long long y2);
void debug_q();
struct graph
{
    long long x,y;
};
struct node
{
    long long visit[MAX_INDEX];
    long long index;
    long long cengshu;
    long long alreadyset;
};
graph luyouqi[MAX_INDEX];
queue <node> q;
long long connected[MAX_INDEX][MAX_INDEX];
long long visit_node_k[MAX_INDEX][MAX_INDEX];
long long n,m,k,r;
long long bfs();
int main()
{
    cin >> n >> m >> k >> r;
    for(long long i = 0; i<n; i++)
        //cin >> luyouqi[i].x >> luyouqi[i].y;
        scanf("%lld%lld",&luyouqi[i].x,&luyouqi[i].y);
    for(long long i = n; i<m+n; i++)
        //cin >> luyouqi[i].x >> luyouqi[i].y;
        scanf("%lld%lld",&luyouqi[i].x,&luyouqi[i].y);
    for(long long i=0; i<MAX_INDEX; i++)
        for(long long j=0; j<MAX_INDEX; j++)
            connected[i][j] = visit_node_k[i][j]= 0;
    for(long long i=0; i<m+n; i++)
        for(long long j=0; j<i; j++)
           if(reach(luyouqi[i].x,luyouqi[i].y,luyouqi[j].x,luyouqi[j].y))
                connected[i][j] =connected[j][i] = 1;
    node temp;
    for(long long i=0; i<m+n; i++)
        temp.visit[i] = 0;
    temp.visit[0] = 1;
    temp.index = 0;
    temp.cengshu = 0;
    temp.alreadyset = 0;
    q.push(temp);
    printf("%ld\n",bfs());
    return 0;
}
bool reach(long long x1,long long y1,long long x2,long long y2)
{
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2) <= r*r;
}
long long bfs()
{
    while(!q.empty())
    {
        node out = q.front();
        //cout << "after pop luyouqi "<< out.index << " " ;
        q.pop();
        //debug_q();
        if(connected[1][out.index])
            return out.cengshu;
        for(long long i=0; i<m+n; i++)
        {
           if(connected[i][out.index]&&!out.visit[i]&&out.alreadyset<=k)
            {
               if(!visit_node_k[i][out.alreadyset])//注意:如果注释掉这里(不使用分析里所说的剪枝策略),会超时
                {
                   visit_node_k[i][out.alreadyset] = 1;
                    node temp= out;
                   temp.visit[i] = 1;
                   temp.cengshu ++;
                   temp.index = i;
                   if(i>=n)
                       temp.alreadyset ++;
                   if(temp.alreadyset<=k)
                       q.push(temp);
                    //cout<< "after push luyouqi " << i << " " ;
                   //debug_q();
                }
            }
        }
    }
}
void debug_q()
{
    node temp;
    printf("the queue is==============\n");
    for(int i = 0; i<q.size(); i++)
    {
        temp = q.front();
        q.pop();
        q.push(temp);
        printf("element %ld :\nindex = %lld,cengshu =%ld,alreadyset = %ld\n",i,temp.index,temp.cengshu,temp.alreadyset);
        for(long long j=0; j<m+n; j++)
            if(temp.visit[j])
                printf("%ld hasbeen visited,\n",j);
    }
    printf("all above==================\n");
}

 

测试样例

10 1 1 2
0 0
3 1
-2 0
-2 2
-2 4
-2 6
0 6
2 6
2 4
2 2
2 0
答案是1


10 1 1 2
0 0
3 1
-2 0
-2 2
-2 4
-2 6
0 6
2 6
2 4
2 2
3 0
答案是8


6 3 2 50000000
0 0
50000000 100000000
100000000 100000000
100000000 0
100000000 50000000
50000000 0
-100000000 50000000
0 50000000
0 100000000
答案是2

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值