清北学堂模拟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; } }