UVa 1609 (博弈) Foul Play

姑且把它归类为一道博弈吧,毕竟这也是在找必胜方案。

十分有意思的一道题目,设计一种方案让你支持的1队获胜。

题目给出了两个很重要的条件:

  • 1队能打败至少一半的队伍
  • 对于1队不能打败的黑队,一定存在一个1队能打败的灰队,使得这支灰队能够打败黑队。也就是说1队可以通过灰队间接打败黑队

一共有2n支队伍,每轮比赛会刷掉一半的队伍,紫书上巧妙的做法就是每轮比赛后让题目给的两个性质依然成立,这样1队最终一定能胜出。

方案如下,大致分为3个阶段:

  1. 物尽其用。依次考虑每个黑队,如果有能够打败他的灰队的话,便让这两只队伍进行比赛。当然,灰队会晋级下一轮。如果没有能够打败当前黑队而且还没有进行比赛的灰队,那么这支黑队进入后面的混战。
  2. 让1队胜出。在1队能够打败的队伍中找到一支还未进行比赛的队伍。对于其他未匹配的队伍,也进入下一步的混战。
  3. 自由混战。黑队和黑队混战,这样能保证至少消灭一半的黑队,顶多有一个黑队剩下。然后剩下的队伍继续混战。

至于为什么这样一定会成功,还请参考紫书。

 1 #include <cstdio>
 2 #include <vector>
 3 using namespace std;
 4
 5 const int maxn = 1030;
 6
 7 char table[maxn][maxn];
 8
 9 int main()
10 {
11     //freopen("in.txt", "r", stdin);
12
13     int n;
14     while(scanf("%d", &n) == 1 && n)
15     {
16         for(int i = 1; i <= n; i++) scanf("%s", table[i] + 1);
17         vector<int> win, lose;
18         for(int i = 2; i <= n; i++)
19         {
20             if(table[1][i] == ‘1‘) win.push_back(i);
21             else lose.push_back(i);
22         }
23
24         int T = n;
25         while(T >>= 1)
26         {
27             vector<int> win2, lose2, final;//进入下一轮1能打败和被打败,以及这一轮混战的队伍
28             bool match;
29             //阶段1:尽可能多的用灰队消灭黑队
30             for(int i = 0; i < lose.size(); i++)
31             {
32                 int black = lose[i];
33                 match = false;
34                 for(int j = 0; j < win.size(); j++)
35                 {
36                     int& gray = win[j];
37                     if(gray > 0 && table[gray][black] == ‘1‘)
38                     {
39                         printf("%d %d\n", black, gray);
40                         match = true;
41                         win2.push_back(gray);//灰队进入下一轮
42                         gray = 0;
43                         break;
44                     }
45                 }
46                 if(!match) final.push_back(black);//进入后面的混战
47             }
48             //阶段2:给1队找个对手
49             match = false;
50             for(int i = 0; i < win.size(); i++)
51             {
52                 int team = win[i];
53                 if(team > 0)
54                 {
55                     if(!match) { printf("1 %d\n", team); match = true; }
56                     else final.push_back(team);//1已经匹配到对手,该队进入混战
57                 }
58             }
59             //阶段3:自由混战,注意到黑队在final中都是挨在一起的
60             for(int i = 0; i < final.size(); i += 2)
61             {
62                 printf("%d %d\n", final[i], final[i + 1]);
63                 int survive = final[i];
64                 if(table[final[i + 1]][final[i]] == ‘1‘) survive = final[i + 1];
65                 if(table[1][survive] == ‘1‘) win2.push_back(survive);
66                 else lose2.push_back(survive);
67             }
68             win = win2;
69             lose = lose2;
70             /*for(int i = 0; i < win.size(); i++) printf("%d ", win[i]);
71             puts("");
72             for(int i = 0; i < lose.size(); i++) printf("%d ", lose[i]);*/
73         }
74     }
75
76     return 0;
77 }

代码君

时间: 2024-10-05 09:25:55

UVa 1609 (博弈) Foul Play的相关文章

UVa 10891 (博弈+DP) Game of Sum

最开始的时候思路就想错了,就不说错误的思路了. 因为这n个数的总和是一定的,所以在取数的时候不是让自己尽可能拿的最多,而是让对方尽量取得最少. 记忆化搜索: d(i, j)表示原序列中第i个元素到第j个元素构成的子序列,先手取数能够得到的最大值. sum(i, j) 表示从第i个元素到第j个元素的和 因为要让对手获得最小的分数,所以状态转移方程为: d(i, j) = sum(i, j) - min{d(枚举所有可能剩给对手的序列), 0(0代表全部取完)} s数组保存a中前i个元素的和,这样s

UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)

题意:有n支队伍(n是2的整数幂,2<=n<=4),打淘汰赛,胜者进入下一轮,其中1号队伍能打败至少一半的队伍,对于它不能打败的队伍l,一定存在一支它能够打败的队伍w,使得w能直接打败l,求一种方案保证1号队伍必胜. 队伍分类:不能直接打败的队伍是黑色队伍,能直接打败且能打败黑色队伍的是灰色队伍. 这里直接给出算法描述了: 阶段1:贪心,每个不能直接打败的队伍,选一个没有匹配的能打败它的灰色队伍. 阶段2:对于剩下的黑色队伍,任意匹配,可能会剩下一个. 阶段3:找一个能直接打败的队伍和1号匹配

uva 1378博弈

算得上是一个比较复杂的游戏了,解法见论文<解析一类组合游戏>,需要注意的是visit数组要适当开大点防止溢出. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 25; 7 int a[N]; 8 int sg[N]; 9 10 void init() 11 { 12 sg[0] = 0; 13 boo

Uva 1609 Feel Good

题面:给出长度为n的数列,然后算出其区间和乘区间最小数所能得到的最大值,并且输出区间 样例输入: 6 3 1 6 4 5 2 样例输出: 60 3 5 原题链接:https://vjudge.net/problem/UVA-1619 分析: 这里有两种算法,一种是O(nlogn)的,用st表+递归,另一种是O(n)的,用单调栈. 容易知道对于数列中的每一个数作为相应区间最小值时,虽然这个相应区间不一定唯一的,但是这个最大区间和一定是唯一的. 举个栗子: 对于数列{0, 0, 0, 0, 0}来说

UVA 10561 - Treblecross(博弈SG函数)

UVA 10561 - Treblecross 题目链接 题意:给定一个串,上面有'X'和'.',可以在'.'的位置放X,谁先放出3个'X'就赢了,求先手必胜的策略 思路:SG函数,每个串要是上面有一个X,周围的4个位置就是禁区了(放下去必败),所以可以以X分为几个子游戏去求SG函数的异或和进行判断,至于求策略,就是枚举每个位置就可以了 代码: #include <stdio.h> #include <string.h> #include <algorithm> usi

Uva 10404-Bachet&#39;s Game(博弈)

题目链接:点击打开链接 在DP专题里刷到的,看着像博弈就水过去了.. 题意:n件物品,两个人轮流取,每次取的数量必须为一个集合s(集合里肯定含有1)里的一个数字,最后不能取者输(即取走最后一件物品者胜). 思路:递推.设 w[i] 为有i件物品时的状态,w[i]=1代表先手必胜,w[i]=0代表先手必败.可以知道w[1]=1,递推生成所有状态. 可以知道对于一个状态,如果他的后继存在必败状态,则该状态为必胜状态:如果该状态的所有后继都为必胜状态,那么该状态为必败状态. #include <alg

UVA 1558 - Number Game(博弈dp)

UVA 1558 - Number Game 题目链接 题意:20之内的数字,每次可以选一个数字,然后它的倍数,还有其他已选数的倍数组合的数都不能再选,谁先不能选数谁就输了,问赢的方法 思路:利用dp记忆化去求解,要输出方案就枚举第一步即可,状态转移过程中,选中一个数字,相应的变化写成一个函数,然后就是普通的博弈问题了,必胜态之后必有必败态,必败态之后全是必胜态 代码: #include <stdio.h> #include <string.h> const int N = 105

UVA 1557 - Calendar Game(博弈dp)

UVA 1557 - Calendar Game 题目链接 题意:给定一个日期,两个人轮流走,每次可以走一月或者一天,问最后谁能走到2001.11.4这个日子 思路:记忆化搜索,对于每个日期,如果下两个状态有一个非必胜态,那么这个状态是必胜态,如果后继状态都是必胜态,那么该状态为必败态 代码: #include <stdio.h> #include <string.h> const int day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31,

UVA 1559 - Nim(博弈dp)

UVA 1559 - Nim 题目链接 题意:一开始有s个石子,2n个人轮流取石子,每个人有个最大能取数目,2n个人奇数一队,偶数一队,取到最后一个石子的队输,问谁赢 思路:记忆化搜索,每个人取的时候对应的后继状态如果有一个必败态,则该状态为必胜态,如果都是必胜态,则该状态为必败态 代码: #include <stdio.h> #include <string.h> int n, s, m[25], dp[25][10005]; int dfs(int now, int state