Holes Gym - 100519H(二分,圖論)

In this problem, we investigate the life of rabbits.

Our rabbits live in the glades, some of which are connected with two-way trails. No trail makes a loop: each of them connects two different glades. There is at most one trail between any pair of glades.

Our rabbits can dig holes in the ground where they can hide in case of danger. The holes can be dug in the center of each glade. In order for the rabbit to hide in the hole, the rabbit just needs to get to the glade where the hole is located, and then it hides there immediately. Each hole can accommodate any number of rabbits.

The rabbit universe has two important features: there is a path between any two glades, and each glades except at most one is connected with no more than two other glades.

Rabbits want to make some holes so that in case of danger, they can minimize the time for all rabbits to hide. Help them find the right glades to dig the holes. The time is calculated as a number of trails a rabbit uses to get to the hole. All rabbits are traveling and hiding independently.

It is also assumed that there are so many rabbits that each glade has at least one of them.

Input
The first line of input contains integers N, M and K: the number of glades, the number of trails and the number of holes to be dug respectively (1 ≤ K ≤ N ≤ 1000).

Each of the next M lines describes one trail and contains two integers x i and y i: the numbers of glades that are connected by this trail (1 ≤ x i, y i ≤ N).

Output
Output a single integer: the minimum possible amount of time needed for all rabbits to hide in case all K holes are placed optimally.

Examples
Input
5 6 1
3 1
3 2
3 4
3 5
1 2
4 5
Output
1
Input
8 8 2
1 2
2 3
3 4
4 1
1 5
5 6
6 7
7 8
Output
2

題意:
一個圖,最多隻有一個點度數大於2。可以放kk個房子,每個點要去一個房子,求所有點找到房子的最小時間。

思路:
將最大度數的點當做根節點,則根節點出發要麼成環回到根節點,要麼就是一條直鏈。

二分這個最大距離,然後算最少需要多少個房子。
可以想到,在一個房子,最多影響2x+12*x+1個點,策略是放到中間

先一遍dfsdfs求出所有直線和環的大小。

對於直鏈與環分別考慮。

mx1mx1代表根節點最多延伸多少個點
mx2mx2代表距根節點最多多少個點沒覆蓋到

初始化mx1=1,mx2=1mx1=-1,mx2=-1
mx1=0mx1=0時代表根節點被延伸到
mx2=0mx2=0時代表根節點沒覆蓋到

一個直線必須的房子數爲:line[i]/(2mid+1)line[i] / (2 * mid + 1)
res=line[i]mod  (2mid+1)res = line[i] \mod (2 * mid + 1)

如果res>mid+1res>mid+1,則這個直線上必須再放一個房子,最後根節點往外延伸的點數爲2mid+1res2 * mid + 1 - res
如果res=0res=0,則代表根節點被延伸到了,所以mx1=max(mx1,0)mx1=max(mx1,0)
否則,則剩下了沒覆蓋到的點,所以mx2=max(mx2,res1)mx2=max(mx2,res-1)

對於環也是這是類似的考慮,但是比較特殊的是,當有circle[i]mod  (2mid+1)circle[i] \mod(2 * mid + 1)時,代表肯定根節點肯定要放房子(這個環要一開始被全覆蓋,根節點放上房子顯然更優),那麼每個環的需要放的房子數位circle[i]/(2mid+1)1;circle[i] / (2 * mid + 1) - 1;,減去的1爲根節點的房子數,最後再加上。

最後考慮根節點上要不要放房子:假設mx2=1mx2=-1,代表沒有點沒被覆蓋到,則不用算。否則比較mx2mx2mx1mx1,如果mx2>mx1mx2>mx1,則延伸的部分不能抵消覆蓋的部分,那麼根節點一定要放房子。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <string>
#include <iostream>
#include <cmath>

using namespace std;
typedef long long ll;

const int maxn = 1000 + 7;

int n,m,k;
vector<int>G[maxn];
int line[maxn],circle[maxn],vis[maxn];
int cnt1,cnt2;

void dfs(int u,int fa,int rt,int d) {
    if(G[u].size() == 1) {
        line[++cnt1] = d;
        return;
    }
    for(int i = 0;i < G[u].size();i++) {
        int v = G[u][i];
        if(v == fa) continue;
        if(v == rt) {
            circle[++cnt2] = d;
            return;
        }
        if(vis[v]) continue;
        vis[v] = 1;
        dfs(v,u,rt,d + 1);
    }
}

bool check(int mid) {
    int cost = 0;
    int mx1 = -1; //根節點最多延伸多少個點
    int mx2 = -1; //距根節點最多多少個點沒覆蓋到
    
    //mx1如果爲0代表延伸到了根節點,如果爲-1代表沒延伸到根節點
    //mx2如果爲0代表根節點沒有覆蓋到,mx2如果爲-1代表所有點都覆蓋到了。
    
    for(int i = 1;i <= cnt1;i++) {
        cost += line[i] / (2 * mid + 1);
        int res = line[i] % (2 * mid + 1);
        if(res - 1 > mid) {
            cost++;
            mx1 = max(mx1,2 * mid + 1 - res); //最遠往外延伸多少
        }
        else if(res == 0) {
            mx1 = max(mx1,0);
        }
        else {
            mx2 = max(mx2,res - 1); //算出剩餘點離根節點的最遠距離
        }
    }
    
    for(int i = 1;i <= cnt2;i++) {
        if(circle[i] % (2 * mid + 1) == 0) {
            cost += circle[i] / (2 * mid + 1) - 1; //因爲最後要用根節點,這裏暫時不用
            mx2 = max(mx2,mid); //表示一定要用根節點
        } else {
            cost += circle[i] / (2 * mid + 1);
            int res = circle[i] % (2 * mid + 1);
            mx2 = max(mx2,res / 2);
        }
    }
    
    if(mx2 != -1 && mx2 > mx1) {
        cost++;
    }
    return cost <= k;
}

int main() {
    scanf("%d%d%d",&n,&m,&k);
    int rt = 1,mx = 0;
    for(int i = 1;i <= m;i++) {
        int x,y;scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
        if(G[x].size() > mx) {
            mx = G[x].size();
            rt = x;
        }
        if(G[y].size() > mx) {
            mx = G[y].size();
            rt = y;
        }
    }
    if(k >= n) {
        printf("0");
        return 0;
    }
    dfs(rt,-1,rt,1);
    
    int l = 1,r = n;
    int ans = n;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid)) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章