uva1609 foul play

A European soccer tournament has n participating teams. In the rst
round, n= 2 matches are played such that each team plays against one
other team. After every round, only the winning teams advance to the
next round. In the second round, n= 4 matches are played such that
each rst-round winner plays against one other rst-round winner.
Eventually, only two teams are left to play a nal match. The winner
of the nal match is the champion of the tournament. The Dutch soccer
team is probably not the best team in the world. However, they are
still a pretty good team. They can easily win from at least half of
the other teams. Moreover, for every team t that the Dutch cannot beat
in a direct confrontation, there is another team t ′ that beats t ,
but is beaten by the Dutch team. The Dutch coach wants to manipulate
the tournament such that the Dutch team will become champion. He
happens to know, for each pair of teams, which team would certainly
win if a match was played between them. Problem For each two teams,
you know beforehand which one would win if they played against each
other. (Since this is a knock-out tournament, no ties will occur.)
Furthermore, you know for sure that your favourite team can beat at
least half of the other teams, and for every team t that your
favourite team cannot beat, there is a team t ′ that beats t but is
itself beaten by your favourite team. Determine a tournament schedule
such that your favourite team wins the tournament. Input For each test
case, the input is as follows: One line containing the number of
teams n , where n is a power of two and 2 n
1024. Teams are numbered from 1 to n , where team 1 is your favourite team. n lines, each containing a string of n binary digits. The k
-th digit on the j
-th line is 1 ' if team j would certainly win from team k , otherwise it is 0 ‘. A team cannot play against itself, therefore
the j
-th digit on the j
-th line is ` 0 ‘. If j
= k , the k
-th digit on the j
-th line is different from the j
-th digit on the k
-th line. Output For each test case, print n

当n=2时,满足“1号能打败至少一半的队伍”,即1号能打败2号,则1号一定夺冠。
因此,只需要保证在每轮过后仍然满足“1号能打败至少一半的队伍,且对于任何1号不能打败的队伍,都有至少一支1号可以打败的队伍可以打败他”,则是一种可行解。
把队伍分为1号能打败的和能打败1号的两类(分别叫A,B)。
首先依次对于A类的每支队伍,在B里找一支能打败他的。不妨将胜出的队伍记为B1,没有被选择的A记为A2,而没有被选择的B记为B2。在这结束以后,B1一定有能力消灭每一个A2。【证明:假设有一个A2能打败所有B1,则根据“对于任何1号不能打败的队伍,都有至少一支1号可以打败的队伍可以打败他”,B2中一定有一支队伍可以打败他。那么,这两支球队就应该配对。矛盾。】这就保证了第二条,“对于任何1号不能打败的队伍,都有至少一支1号可以打败的队伍可以打败他”到下一轮时仍然满足。
接下来,在B2中随便挑选一个,与1配对。
再接下来,把A2自由相互配对。
最后,剩下的B2和可能剩下的一个A2(如果A2有奇数个)自由配对。
在这结束之后,第一阶段中消灭了所有被配对的A,而第三阶段消灭了一半的剩下的A。因此A的总数至少减少一半。而这一轮过后队伍总数也减半,所以仍然满足“1号能打败至少一半的队伍”。
综上,这是一个可行解。
虽然过程看上去比较繁琐,但实际上代码还算比较好写。

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
bool beat[1100][1100];
vector<int>::iterator i1,i2;
int main()
{
    int i,j,k,l,m,n,p,q,x,y,z;
    bool b;
    char s[1100];
    while (scanf("%d",&n)==1)
    {
        vector<int> left;
        for (i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            for (j=1;j<=n;j++)
              beat[i][j]=s[j]-'0';
        }
        for (i=2;i<=n;i++)
          left.push_back(i);
        while (n>1)
        {
            n>>=1;
            vector<int> left1,left2,final,win,lose;
            for (i=0;i<left.size();i++)
              if (beat[1][left[i]]) win.push_back(left[i]);
              else lose.push_back(left[i]);
            for (i=0;i<lose.size();i++)
            {
                b=0;
                for (j=0;j<win.size();j++)
                  if (win[j]>0&&beat[win[j]][lose[i]])
                  {
                    printf("%d %d\n",win[j],lose[i]);
                    b=1;
                    final.push_back(win[j]);
                    win[j]=-1;
                    break;
                  }
                if (!b) left1.push_back(lose[i]);
            }
            for (i=0;i<win.size();i++)
              if (win[i]>0)
              {
                printf("1 %d\n",win[i]);
                win[i]=-1;
                break;
              }
            for (i=0;i+1<left1.size();i+=2)
            {
                printf("%d %d\n",left1[i],left1[i+1]);
                if (beat[left1[i]][left1[i+1]]) final.push_back(left1[i]);
                else final.push_back(left1[i+1]);
            }
            if (left1.size()%2) left2.push_back(left1[left1.size()-1]);
            for (i=0;i<win.size();i++)
              if (win[i]>0) left2.push_back(win[i]);
            for (i=0;i+1<left2.size();i+=2)
            {
                printf("%d %d\n",left2[i],left2[i+1]);
                if (beat[left2[i]][left2[i+1]]) final.push_back(left2[i]);
                else final.push_back(left2[i+1]);
            }
            left=final;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值