清北学堂模拟day6 兔子

【问题描述】

在一片草原上有N个兔子窝,每个窝里住着一只兔子,有M条路径连接这些窝。更特殊地是,至多只有一个兔子窝有3条或更多的路径与它相连,其它的兔子窝只有1条或2条路径与其相连。换句话讲,这些兔子窝之前的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。

兔子们决定把其中K个兔子窝扩建成临时避难所。当危险来临时,每只兔子均会同时前往距离它最近的避难所躲避,路程中花费的时间在数值上等于经过的路径条数。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造避难所的方式,使最后一只到达避难所的兔子所花费的时间尽量少。

 

【输入】

       第一行有3个整数N,M,K,分别表示兔子窝的个数、路径数、计划建造的避难所数。

接下来M行每行三个整数x,y,表示第x个兔子窝和第y个兔子窝之间有一条路径相连。任意两个兔子窝之间至多只有1条路径。

 

【输出】

一个整数,表示最后一只到达避难所的兔子花费的最短时间。

 

【输入输出样例1】

rabbit.in

rabbit.out

5 5 2

1 2

2 3

1 4

1 5

4 5

1

       见选手目录下的rabbit / rabbit1.in与rabbit / rabbit1.out

 

【输入输出样例1说明】

       在第2个和第5个兔子窝建造避难所,这样其它兔子窝的兔子最多只需要经过1条路径就可以到达某个避难所。

 

【输入输出样例2】

       见选手目录下的rabbit / rabbit2.in与rabbit / rabbit2.out

 

【数据规模与约定】

       对于30%的数据,N≤15,K≤4;

       对于60%的数据,N≤100;

       对于100%的数据,1≤K≤N≤1,000,1≤M≤1,500

/*
求到达时间最晚的兔子的最早到达时间
二分答案X,求解至少建几个避难所,使得每个兔窝在距离X的范围内至少有一个避难所
枚举住在“根”的兔子去往的避难所的位置,记为A
令与A距离不超过X的兔子都前往A
剩下的兔窝被分成若干条链,容易计算最少需要建立几个避难所
判断总数是否超过K个
*/
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int maxn = 2010;
struct data {
    int adj, next;
} r[10 * maxn];
int g[maxn], tot, du[maxn], rt;
void ins(int a, int b)
{
    r[++tot].adj = b;
    r[tot].next = g[a];
    g[a] = tot;
}
int n, m, k;

struct road {
    bool circle;
    int len;
};
road d[maxn], d2[maxn];
int cnt;

int calc(int len, int t)//计算链中需要建造的避难所的数量,每t*2+1个点建一个 
{
    if (len <= 0) return 0;
    return (len - 1) / (t * 2 + 1) + 1;
}

bool check(int t)
{
    int mind = n + 1;
    for (int i = 0; i < cnt; ++i)
    {
        for (int kp = 0; kp <= d[i].len && kp <= t; ++kp)
        {
            int tot;
            if (!d[i].circle)
                tot = calc(d[i].len - kp - t, t);
            else tot = calc(d[i].len - kp - t + (kp - t), t);//注意环的计算,实际上避难所A可以影响到深度比较大的节点 
            ++tot;
            for (int j = 0; j < cnt; ++j)
            if (j != i)
            {
                if (!d[j].circle)
                    tot += calc(d[j].len + (kp - t), t);//注意A对他的影响 
                else
                    tot += calc(d[j].len + 2 * (kp - t), t);
            }
            mind = min(mind, tot);
        }
    }
    return mind <= k;
}

void binary()
{
    int l = 0, r = n;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d\n", r);
}

void init();
int main()
{
    freopen("rabbit.in", "r", stdin);
    freopen("rabbit.out", "w", stdout);
    
    init();
    binary();
    return 0;
}
bool col[maxn];
int t0;
void dfs(int x)
{
    col[x] = 1;
    ++d[cnt].len;
    for (int p = g[x]; p != -1; p = r[p].next)
        if (col[r[p].adj] == 0)
            dfs(r[p].adj);
        else if (r[p].adj == rt && x != t0)
            d[cnt].circle = 1;
}
void init()
{
    scanf("%d%d%d", &n, &m, &k);
    memset(g, 255, sizeof(g));
    tot = -1;
    memset(du, 0, sizeof(du));
    rt = 1;
    for (int i = 1; i <= m; ++i)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        ++du[a];
        if (du[a] > 2) rt = a;
        ++du[b];
        if (du[b] > 2) rt = b;
        ins(a, b);
        ins(b, a);
    }
    cnt = 0;//从根伸出去的链+环的数量 
    memset(col, 0, sizeof(col));
    col[rt] = 1;//访问标记 
    for (int i = g[rt]; i != -1; i = r[i].next)
        if (col[r[i].adj] == 0)
        {
            t0 = r[i].adj;//用于判环 
            d[cnt].circle = 0;//是否在环中 
            d[cnt].len = 0;//此链Or环的长度(可以想成是点的数量) 
            dfs(r[i].adj);
            ++cnt;
        }
}

 

posted @ 2016-10-16 19:24  ACforever  阅读(276)  评论(0编辑  收藏  举报