POJ-1818 ATP ****

 1 /*
2 * 贪心 + 二分
3 * 看了网上的代码 + discuss 才想出解法。。。
4 *
5 * 设答案是排名为p的人
6 * 则贪心策略是: p最后一轮与(p-k)比赛。
7 * 这个还是有点显然的,因为所有与p比赛且能被p赢的人中,
8 * (p-k)是最容易撑到最后一轮的。
9 * 证明: 否则,设p最后一轮与m比赛,(则m > p-k),(p-k)在某一轮与a比赛时被打败,
          同时这一轮m是与b比赛且m胜利(因为m撑到了最后一轮)。则我们交换这一轮中的(p-k)和m,
          即这一轮中让m与a比赛,因为(p-k)能被a打败,m也一定能被a打败;(p-k)与b比赛,(p-k)胜利。
          这样,在后续的安排中,m的位置都改为(p-k)且(p-k)撑到最后一轮。其他都没有发生变化。
          因此按贪心策略的比赛安排是存在的。
10 *
11 * 有了以上结论,就可以“递归”的安排比赛。如(p-k)在倒数第二轮优先选择和(p-k-k)比赛。。。
12 *
13 * 二分最后胜利的人,按以上规则判断是否可行。
14 *
15 * 以下代码乃网上某大牛的,直接转过来。。。(加了注释)
16 *
17 */
18
19
20 #include <stdio.h>
21 #include <cstring>
22 #define max(a,b) ((a)>(b)?(a):(b))
23
24 int n, x, k, queue[5010], add; char elio[5010];
25
26 int vali (int person)
27 {
28 int i, j, kase, tail, t;
29 memset(elio, 0, sizeof(elio));
30 //可以认为:elio[i]=j 表示i最后一次胜利是在倒数第j轮
31 elio[person] = 1; tail = person;
32 add = 0; queue[add++] = person;
33 //总共x轮 (这里kase=i实际上表示倒数第i轮比赛)
34 for (kase = 1; kase <= x; kase++)
35 {
36 t = add;
37 for (i = 0; i < t; i++)
38 {
39 for (j = max(1, queue[i] - k); j <= tail; j++)
40 {
41 if (elio[j] == 0)
42 {
43 elio[j] = kase + 1;
44 queue[add++] = j; //放入数组,因为下一轮循环(即上一轮比赛)需要为其分配对手
45 break;
46 }
47 }
48 }
49 }
50 for (i = 1; i <= tail; i++)
51 if (elio[i] == 0) return 0; //之前还有人没分配到,即没有合法方案
52 return 1;
53 }
54
55 int main ()
56 {
57 int st, ed, mid, i;
58 scanf("%d %d", &n, &k);
59 for (x = 0, i = 1; i < n; x++, i *= 2);
60 st = 1, ed = n;
61 if (vali(ed)) st = ed;
62 else while (ed - st > 1) //二分
63 {
64 mid = (st + ed) >> 1;
65 if (vali(mid)) st = mid;
66 else ed = mid;
67 }
68 printf("%d\n", st);
69 return 0;
70 }

你可能感兴趣的:(poj)