SJTU OJ 3046 足球 题解

3046. 足球

Description

众所周知,一只足球队由11球员组成,其中有一名守门员和一名队长。 现在,有22人组成的一个候选名单,从中要选出11人参加比赛。选拔规则如下: 首先提供一个阵型,例如,4-4-2。第一个4表示有4名后卫,第二个4表示有4名中场,第三个2表示有2名前锋。当然,还有一个位置就是留给守门员的。 每个人都有自己唯一能打的位置(前锋、中场、后卫、守门员)。在每个位置上,以球员的编号为关键字,从小到大依次选取。 选出球队后还要选出队长。队长是球队中参加比赛次数最多的球员。如果有并列,取编号较大的球员做队长。

Input Format

前22行每行按如下格式表示出一名球员的信息:
Number Name Role year1-year’1 year2-year’2 …
整数Number(<=100)是该球员编号。字符串Name(length<=20)是该球员名字。字符Role是该球员所能打的位置(S前锋、M中场、D后卫、G守门员)。每一对yeari-year’i(yeari<=year’i) 表示该球员效力的时间段,例如2001-2002表示该球员效力了两年2001年和2002年。至少有1对年份,最多20对年份。年份是一个四位数。保证任意两段年份不会重叠。球员的编号在球队中是唯一的。
第23行给出一个阵型,例如4-4-2。保证阵型有且仅有3个正整数组成,且和等于10。例如,4-3-2-1是不合法的。

Output Format

输出选出的11名球员,每一行包括球员编号,姓名,场上所打位置。之间用一个空格隔开。球员按守门员、后卫、中场、前锋的顺序排序,相同位置按编号从小到大排序。如果一名球员是队长,那么无论他打什么位置,都放在序列的第一个。如果无法找出11个满足条件的球员,输出“IMPOSSIBLE TO ARRANGE”。

Sample Input

9 PlayerA M 2000-2001 2003-2006
2 PlayerB M 2004-2006
10 PlayerC D 2001-2005
1 PlayerD D 2000-2001 2002-2004
11 PlayerE S 2003-2006
8 PlayerF M 2005-2006
22 PlayerG S 2005-2006
25 PlayerH G 2000-2001 2002-2003 2005-2006
6 PlayerI D 2003-2006
26 PlayerJ D 2003-2004 2000-2001
18 PlayerK M 2003-2004
19 PlayerL M 2000-2001 2003-2006
7 PlayerM S 2003-2006 1999-2001
21 PlayerN S 2003-2006
13 PlayerO S 2005-2006
15 PlayerP G 2001-2006
14 PlayerQ D 2003-2004
5 PlayerR S 2000-2005
20 PlayerS G 2000-2002 2003-2003
12 PlayerT M 2004-2005
3 PlayerU D 2000-2005
4 PlayerZ M 2001-2004
4-4-2

Sample Output

7 PlayerM S
15 PlayerP G
1 PlayerD D
3 PlayerU D
6 PlayerI D
10 PlayerC D
2 PlayerB M
4 PlayerZ M
8 PlayerF M
9 PlayerA M
5 PlayerR S

Limits

Time limit: 1000ms, memory limit: 50000kb.


===============================================================

题解正文

    ===============================================================

  1. 题目分析:

    首先我们要知道这道题目给了什么信息:

    1. 这是张由22个球员构成的表。

    2. 每个主球运动员都有 编号(唯一)、名字(没有用)、位置(D M S G)、年份(这个年份纯粹是搞出来糊弄人的,我们只要通过一定手段算出来到底有多少年即可)

    3. 这题很好的地方是年份一定是四位数并且一定是长度为9的字符串:XXXX-XXXX,这个非常便于我们计算该运员干了多少年,干的年份越久表示参加的比赛越多 = =。

    4. 输出的要求其实也很简单,满足阵型是首要条件,接下来是选取相应的人,注意到同位置选取编号小的;

    5. 选队长时优先选年份多的,再次是编号大的(和上面不同)。

  2. 算法实现:

    1. 输入

      1. 考虑到运动员这一独立的对象,又懒得写类,于是用struct来构造一个player,这个struct叫ply

      2. struct ply{
            int num;  //编号
            string name; //名字
            char pst; //位置
            int years; //年份
            bool used; //是否已经被选取,必要性不太大,保险起见而已
        }players[22],team[11]; //左边是备选的22个备胎,右边是被选中的11个孩子
      3. 上面提到的pst其实用枚举类型更好,直接给那几个位置赋G0 D1 M2 S3 这样输出时排列非常方便

      4. 输入流处理:

      5. void init()
        {
            cin >> players[0].num; //这里可以看到我编号是额外处理的,这是因为读取年份时为了简便
            string tmp;            //不得不多读一个字符串,导致下一个球员的编号被读入了tmp中
            for(int i = 0; i < 22; ++i)
            {
                cin >> players[i].name  //直接存就好了
                >>players[i].pst;
                players[i].years = 0;
                while(cin >> tmp && tmp.length() == 9) //这就是为了及时停下来,但无可避免地
                {                                      //读取了下一个球员的编号
                    int a, b;
                    a = 1000 * (tmp[0] - '0') +       //计算年份
                    100 * (tmp[1] - '0') +
                    10 * (tmp[2] - '0') + (tmp[3] - '0');
                    b = 1000 * (tmp[5] - '0') +
                    100 * (tmp[6] - '0') +
                    10 * (tmp[7] - '0') + (tmp[8] - '0');
                    players[i].years += (b - a) + 1; //注意这个加一,小学常见的应用题陷阱
                }
                if(i < 21) players[i+1].num = (int)atof(tmp.c_str());//string直接转成int
                players[i].used = 0; //初始化
            }
         
            sort(players, players + 22, cmp1); //排序优先位置升序,其次编号升序
            cin >> tmp;
            D = tmp[0] - '0';
            M = tmp[2] - '0';
            S = tmp[4] - '0';
        }
      6. 详细讲一下cmp的使用,我使用了三个cmp,分别有不同的用处,他们极大地维护了代码的简洁和准确

      7. inline bool cmp1(ply a, ply b)
        {  //优先位置升序,其次编号升序
            if(a.pst == b.pst)
                else return a.num < b.num;
            else return a.pst < b.pst;
        }
        //选队长专用
        inline bool cmp2(ply a, ply b)
        {    //优先年份,年份一样按大序号优先
            if(a.years == b.years ) return a.num > b.num;
            else return a.years > b.years;
        }
         //最后除了队长之外的人进行排序
        inline bool cmp3(ply a, ply b)
        {
            if(a.pst == 'G')return 1;//这两条确保后卫排在最前面
            if(b.pst == 'G')return 0;
            else//恰好利用了ascii中D < M < S 这一原则 不过如果输出顺序不按GDMS 我们也可以通过
                //enum来安排顺序值
            if(a.pst == b.pst) return a.num < b.num;
            else return a.pst < b.pst;
         
        }
    2. 选拔

      1. 选拔就比较简单了,主要是用了一个变量t来引导球员储存到team中并且计算一共有多少球员满足要求,当且仅当t==11时满足要求,因此可以直接用t来判断是否有解,我第一次提交就是忘记考虑无解的情况了

      2. 另外输入的时候读取了四个位置所要求的值(当然G默认为1)通过while循环达到读取的目的,不会漏掉也不会多取。

      3. 具体请看最终代码

    3. 输出

      输出很简单了。如果有解就对team数组进行排序,然后打印出来。无解就输出那句话即可。

  3. 代码送上,有很多不足,大家看看就好~欢迎讨论

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include<stdlib.h>
#include<stdio.h>
 
using namespace std;
 
struct ply{
    int num;
    string name;
    char pst;
    int years;
    bool used;
}players[22],team[11];
int D, M, S, G, L;
bool flag = 1;
inline bool cmp1(ply a, ply b)
{
    if(a.pst == b.pst)
    {
        if(a.num == b.num)
        {
            if(a.years == b.years) return a.name < b.name;
            else return a.years > b.years;
        }
        else return a.num < b.num;
    }else return a.pst < b.pst;
}
inline bool cmp2(ply a, ply b)
{
    if(a.years == b.years ) return a.num > b.num;
    else return a.years > b.years;
}
 
inline bool cmp3(ply a, ply b)
{
    if(a.pst == 'G')return 1;
    if(b.pst == 'G')return 0;
    else
    if(a.pst == b.pst) return a.num < b.num;
    else return a.pst < b.pst;
 
}
void init()
{
    cin >> players[0].num;
    string tmp;
    for(int i = 0; i < 22; ++i)
    {
        cin >> players[i].name
        >>players[i].pst;
        players[i].years = 0;
        while(cin >> tmp && tmp.length() == 9)
        {
            int a, b;
            a = 1000 * (tmp[0] - '0') +
            100 * (tmp[1] - '0') +
            10 * (tmp[2] - '0') + (tmp[3] - '0');
            b = 1000 * (tmp[5] - '0') +
            100 * (tmp[6] - '0') +
            10 * (tmp[7] - '0') + (tmp[8] - '0');
            players[i].years += (b - a) + 1;
        }
        if(i < 21) players[i+1].num = (int)atof(tmp.c_str());
        players[i].used = 0;
    }
 
    sort(players, players + 22, cmp1);
    cin >> tmp;
    D = tmp[0] - '0';
    M = tmp[2] - '0';
    S = tmp[4] - '0';
}
void select()
{
    int t = 0;
    for(int i = 0; i < 22; ++i)
    {
        while(D && players[i].pst == 'D')
        {
            team[t++] = players[i];
            ++i, --D;
        }
        if(players[i].pst == 'G')
            team[t++] = players[i];
        while(players[i].pst == 'G')++i;
        while(M && players[i].pst == 'M')
        {
            team[t++] = players[i];
            ++i, --M;
        }
        while(S && players[i].pst == 'S')
        {
            team[t++] = players[i];
            ++i, --S;
        }
    }
    if(t == 11)
    {
        sort(team, team + 11, cmp2);
        sort(team + 1, team + 11, cmp3);
    }else flag = 0;
}
int main()
{
    init();
    select();
    if(flag)
        for(int i = 0; i < 11; ++i)
            cout<<team[i].num<<" "<<team[i].name<<" "<<team[i].pst<<endl;
    else cout<<"IMPOSSIBLE TO ARRANGE"<<endl;
 
 
}





转载于:https://my.oschina.net/xueyang/blog/288014

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值