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;
}

上一篇:我的TCP保活机制(TCP_KEEPALIVE)学习心得--从入门到入土


下一篇:setsockopt和getsockopt函数