1. 程式人生 > >CCF201403-4 無線網路(100分)

CCF201403-4 無線網路(100分)

試題編號: 201403-4
試題名稱: 無線網路
時間限制: 1.0s
記憶體限制: 256.0MB
問題描述: 問題描述   目前在一個很大的平面房間裡有 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

問題描述:(參見上文)。

問題分析:這是一個求最優問題,通常用BFS(廣度優先搜尋)來實現。據稱,該問題還可以用SPFA演算法來實現。

程式說明:陣列visited[]用於標記訪問過的座標。函式bfs()的引數是為程式通用性而設定的,就本問題而言,可以使用常量。

網友指出,程式有錯,並且給出了錯誤的樣例。據此修改了程式,後一個程式才是正解

CCF的線上評判系統中的測試資料常常不夠充分,程式有BUG照樣得100分。

後一個程式增加了增設路徑器的計數,如果已經用過2個,則以後不得再用增設路由器,這時只在已經設定好的路由器中搜索。參見程式中的17行的程式碼、50-53行的程式碼、以及70-73行的程式碼。

提交後得100分的C++語言程式如下(有BUG的100分)

/* CCF201403-4 無線網路 */

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 100 + 100;

struct {
    long long x, y;
} coord[N+1];

struct node {
    long long x, y;
    int step;
};

bool visited[N+1];

int bfs(int n, int begin, int end, long long r)
{
    // 變數初始化
    memset(visited, false, sizeof(visited));

    // 設定根結點
    node start, front, v;
    start.x = coord[begin].x;
    start.y = coord[begin].y;
    start.step = 0;
    queue<node> q;
    q.push(start);

    // 設定根結點為已經訪問過
    visited[begin] = true;

    while(!q.empty()) {
        front = q.front();
        q.pop();

        // 到達終點則結束
        if(front.x == coord[end].x && front.y == coord[end].y)
            return front.step - 1;

        // 搜尋可以連線的路由器
        for(int i=0; i<n; i++) {
            // 訪問過的座標則跳過
            if(visited[i])
                continue;

            // 判定下一個路由器的座標是否在半徑r之內, 不在半徑之內則跳過,在半徑之內則繼續搜尋
            if((front.x - coord[i].x) * (front.x - coord[i].x) + (front.y - coord[i].y) * (front.y - coord[i].y) > r * r)
                continue;
            else {
                // 第i個路由器設為已經訪問過
                visited[i] = true;

                // 計算步數,並且將第i個路由器加入佇列
                v.x = coord[i].x;
                v.y = coord[i].y;
                v.step = front.step + 1;
                q.push(v);
            }
        }
    }

    return 0;
}

int main()
{
    int n, m, k;
    long long r;

    // 輸入資料
    cin >> n >> m >> k >> r;
    for(int i=0; i<n+m; i++)       // n個路由器的位置+可以增設的m個路由器的位置
        cin >> coord[i].x >> coord[i].y;

    // BFS
    int ans = bfs(n + m, 0, 1, r);

    // 輸出結果
    cout << ans << endl;

    return 0;
}

/*
測試資料:

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

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
*/

提交後得100分的C++語言程式如下(正解):

/* CCF201403-4 無線網路 */

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 100 + 100;

struct {
    long long x, y;
} coord[MAXN+1];

struct status {
    long long x, y;
    int step, kcount;
};

bool visited[MAXN+1];

int dfs(int n, int m, int k, int begin, int end, long long r)
{
    int max;

    // 變數初始化
    memset(visited, false, sizeof(visited));

    // 設定根結點
    status start, front, v;
    start.x = coord[begin].x;
    start.y = coord[begin].y;
    start.step = 0;
    start.kcount = 0;
    queue<status> q;
    q.push(start);

    // 設定根結點為已經訪問過
    visited[begin] = true;

    while(!q.empty()) {
        front = q.front();
        q.pop();

        // 到達終點則結束
        if(front.x == coord[end].x && front.y == coord[end].y)
            return front.step - 1;

        // 搜尋可以連線的路由器
        if(front.kcount == k)
            max = n;
        else
            max = n + m;
        for(int i=0; i<max; i++) {
            // 訪問過的座標則跳過
            if(visited[i])
                continue;

            // 判定下一個路由器的座標是否在半徑r之內, 不在半徑之內則跳過,在半徑之內則繼續搜尋
            if((front.x - coord[i].x) * (front.x - coord[i].x) + (front.y - coord[i].y) * (front.y - coord[i].y) > r * r)
                continue;
            else {
                // 第i個路由器設為已經訪問過
                visited[i] = true;

                // 計算步數,並且將第i個路由器加入佇列
                v.x = coord[i].x;
                v.y = coord[i].y;
                v.step = front.step + 1;
                if(i >= n)
                    v.kcount = front.kcount + 1;
                else
                    v.kcount = front.kcount;
                q.push(v);
            }
        }
    }

    return 0;
}

int main()
{
    int n, m, k;
    long long r;

    // 輸入資料
    cin >> n >> m >> k >> r;
    for(int i=0; i<n+m; i++)       // n個路由器的位置+可以增設的m個路由器的位置
        cin >> coord[i].x >> coord[i].y;

    // BFS
    int ans = dfs(n, m, k, 0, 1, r);

    // 輸出結果
    cout << ans << endl;

    return 0;
}

/*
測試資料:

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

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
*/