UVa1609 Foul Play 不公平竞赛 (直接构造法)紫书训练
作者:互联网
题意:
n支队伍打淘汰赛,每轮都是两两配对,胜者进入下一轮。
每支队伍的实力固定,并且已知每两支队伍间的比赛结果(不可能为平局,则一定有一方打败另一方),你喜欢一号队,但是一号队不一定是最强的,但是他可以直接打败其他队伍中的至少一半,并且对于每支一号队不能打败的队伍t,总是存在一支1号队能直接打败的队伍t’ 使得t’ 能直接打败t,输出比赛安排。先输出最底层的,比如n = 8 先输出 8场第一轮比赛 4场第二轮比赛 2 场第三轮比赛 1场第四轮比赛。
分析:
紫书上p249写的够明白了,我认为也没啥必要写,就自己翻书看吧。
个人做题感受:这题不看分析感觉一点头绪都没有,看了分析之后感觉还是一头雾水,我一开始写了半天还以为这四个阶段结束就够了,结果这只是一轮的内容,本身自身编程基础不是特别好,所以最后照着lrj的分析模拟还是写了很久,我是真感受到直接构造法没啥逻辑可寻,就是看自己的想法对不对题意。这道题就权当对vector的用法的进一步掌握和模拟题目的进一步掌握吧,有空回来再看看。
下面是我自己写的很丑的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1024+5;
char a[maxn][maxn];
int vis[maxn];//用来检测是否这个队伍是否比过赛
vector<int>win[maxn], lose[maxn];
//win[i][j]用来统计第i个队能打得过的队伍,lose[i][j]用来统计第i个队伍打不过的队伍
void step1(int n)
{
int i, j;
for ( i = 0; i < lose[1].size(); i++)//第一阶段从1号队伍打不过的队伍开始遍历
{
for ( j = 0; j < lose[lose[1][i]].size(); j++)//lose[lose[1][i]][j]表示1号队打不过的队伍的打不过的队伍,稍微有点绕,也就是能打过(1号队打不过的队伍)的队伍
{
if (!vis[lose[lose[1][i]][j]]&&!vis[lose[1][i]]&&a[1][lose[lose[1][i]][j]]=='1') { vis[lose[lose[1][i]][j]] = 2; vis[lose[1][i]] = 1; printf("%d %d\n", lose[lose[1][i]][j], lose[1][i]); break; }
//假如2个队伍都没比过赛,
//又是灰色队伍(a[1][lose[lose[1][i]][j]]=='1'是用来判断该队伍是否为lrj书里写的灰色队伍),
//那么就可以用来比赛,直接输出即可,
//让赢的队伍vis值变成2,可以在一轮结束之前不让该队伍再比一次,一轮结束让vis值变为0
}
}
}
void step2(int n)
{
int i, j;
for (i = 0; i < win[1].size(); i++)
{
if (!vis[win[1][i]]) { vis[win[1][i]] = 1; vis[1] = 2; printf("1 %d\n", win[1][i]); break; }
//1号队伍随便匹配一个灰色队伍,完成第二阶段
}
}
void step3(int n)
{
int i, j;
for (i = 0; i < lose[1].size(); i++)//黑色队伍自相残杀
{
for (j = 0; j < lose[1].size(); j++)
{
if (!vis[lose[1][j]] && !vis[lose[1][i]]&&a[lose[1][i]][lose[1][j]]=='1') { vis[lose[1][j]] = 1; vis[lose[1][i]] = 2; printf("%d %d\n", lose[1][i], lose[1][j]); break; }
//a[lose[1][i]][lose[1][j]]=='1'这句话用来判断i是否打的过j,判断谁赢就把谁的vis值变成2
}
}
}
void step4(int n)
{
int i, j;
for (i = 1; i <= n; i++)//第四阶段剩下队伍随机匹配即可
{
for (j = 1; j <= n; j++)
{
if (!vis[i] && !vis[j] && i != j && a[i][j] == '1') { vis[i] = 2; vis[j] = 1; printf("%d %d\n", i, j); break; }
//a[i][j]用来判断i是否打得过j,和第三阶段的判断是一样的
}
}
for (i = 1; i <= n; i++)
if (vis[i] == 2)vis[i] = 0;
}
int main()
{
int i, n, j;
while (cin >> n)
{
memset(vis, 0, sizeof(vis));
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
cin >> a[i][j];
if (a[i][j] == '1') { win[i].push_back(j); }
else if (a[i][j] == '0' && i != j)lose[i].push_back(j);
}
}
int nt = n;
while (nt > 1)//一共log2n轮
{
step1(n);
step2(n);
step3(n);
step4(n);
nt >>= 1;
}
for (i = 1; i <= n; i++)//清空所有vector,下一个例子再用
{
win[i].clear();
lose[i].clear();
}
}
return 0;
}
下面放出lrj老师的版本,有我自己写的对该版本代码的理解:
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 1024 + 5;
char table[maxn][maxn];
int main() {
int n;
while (scanf("%d", &n) == 1) {
for (int i = 1; i <= n; i++) scanf("%s", table[i] + 1);
vector<int> win, lose; // win和lose分别队伍1打的过的队伍以及打不过的队伍
for (int i = 2; i <= n; i++)
if (table[1][i] == '1') win.push_back(i);
else lose.push_back(i);
int nt = n;
while (nt > 1) {//进行log2n轮
vector<int> win2, lose2, final; //分别表示进入第二轮的1号队伍打得过的队伍和打不过的队伍,final代表进入第三和第四阶段的队伍
// 第一阶段
for (int i = 0; i < lose.size(); i++) {
int tlose = lose[i];
bool matched = false;
for (int j = 0; j < win.size(); j++) {
int& twin = win[j];//利用了c++的引用,twin代表win[j]是否匹配过,匹配过变成0
if (twin > 0 && table[twin][tlose] == '1') {
printf("%d %d\n", twin, tlose);
win2.push_back(twin); //进入下一轮
twin = 0; // 将0的值传回给win[j],防止再次匹配
matched = true;//已经匹配比赛过了
break;
}
}
if (!matched) final.push_back(tlose); //没比赛过的黑色队伍直接送去第三和第四阶段,伏笔
}
// 第二阶段
bool first = true;
for (int i = 0; i < win.size(); i++) {
int twin = win[i];
if (twin > 0) {
if (first) { printf("1 %d\n", twin); first = false; }
else final.push_back(twin);//其他没比赛过的灰色队伍送去第三和第四阶段
}
}
// 第三和第四阶段
for (int i = 0; i < final.size(); i += 2) {
printf("%d %d\n", final[i], final[i + 1]);//第一阶段的伏笔回收,因为前面先加入的黑色队伍,然后再加入的灰色队伍,所以先是黑色队伍自相残杀,然后剩下队伍任意匹配
int keep = final[i];
if (table[final[i + 1]][keep] == '1') keep = final[i + 1];//如果final[i+1]打得过final[i],就让final[i+1]进入第二轮
if (table[1][keep] == '1') win2.push_back(keep);//一号队伍能打得过的队伍进入win数组,并且进入第二轮
else lose2.push_back(keep);//同上
}
win = win2;//将win2的数据放回win,进行递归。
lose = lose2;
nt >>= 1;
}
}
return 0;
}
标签:Play,紫书,int,win,UVa1609,vis,队伍,lose,final 来源: https://blog.csdn.net/m0_52834059/article/details/112059329