• UVA


    /*
      法一:
    
      代码借鉴修改自blog:
      http://blog.csdn.net/aozil_yang/article/details/50543965
      这个博客把思路和注意的地方说得很详细了,建议一看
      
      收获:
      1. 如何在字符串中招某个特定的字符?
      可用strchr()函数:
      http://blog.csdn.net/tommy_wxie/article/details/7554263
      
      2. 浮点数精度的控制 +eps 以及eps的选取:
      http://www.cnblogs.com/oyking/p/3959905.html
      http://www.cnblogs.com/acsmile/archive/2011/05/09/2040918.html
      
      3.fgets()函数 代替容易缓冲溢出的 gets()函数:
      http://www.cnblogs.com/aexin/p/3908003.html
      
      4.博主用了一个很巧妙的思路:将70个专业选手的奖金比例分配完之后,其他的选手都标记为业余选手,这样在控制是否输出奖金时,就会更方便。
      
      但是在循环中,这步就不太好处理了,很容易在边界上出错,考虑时必须谨慎周密!
    
    */



    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 200;
    const int maxm = 50;
    const double eps = 1e-8;
    const int DQ = 0x3f3f3f3f;
    
    int get_len(int x)
    {
    	int ans = 0;
    	while (x)
    	{
    		ans++;
    		x /= 10;
    	}
    	return ans;
    }
    
    struct player
    {
    	char name[maxm];
    	double money;
    	int rk, all2, all4, sco[5];
    	bool is_pro, is_T;
    }players[maxn];
    
    //先按各轮得分排序,再按名字排序
    bool cmp1(const player &a, const player& b)
    {
    	if (a.all2 != b.all2) return a.all2 < b.all2;
    	return strcmp(a.name, b.name) < 0;
    }
    
    bool cmp2(const player &a, const player &b)
    {
    	if (a.all4 != b.all4) return a.all4 < b.all4;
    	return strcmp(a.name, b.name) < 0;
    }
    
    bool cmp3(const player &a, const player &b)
    {
    	int cnta = 0, cntb = 0;
    	for (int i = 0; i < 4; i++)
    	{
    		if (a.sco[i] == DQ)
    		{
    			cnta = i;
    			break;
    		}
    	}
    	
    	for (int i = 0; i < 4; i++)
    	{
    		if (b.sco[i] == DQ)
    		{
    			cntb = i;
    			break;
    		}
    	}
    	
    	if (cnta != cntb) return cnta > cntb;//轮数大的排前面
    	if (a.all4 != b.all4) return a.all4 < b.all4;//分数小的排前面
    	return strcmp(a.name, b.name) < 0;//名字是按字典序排
    }
    int main()
    {
    	int T, n;
    	double money_sum, per[maxn]; // array for percentage
    	scanf("%d", &T);
    	while (T--)
    	{
    		memset(players, 0, sizeof(players));
    		memset(per, 0, sizeof(per));
    		
    		scanf("%lf", &money_sum);
    		for (int i = 0; i < 70; i++) scanf("%lf", per + i);
    		
    		scanf("%d", &n);
    		getchar();
    		for (int i = 0; i < n; i++)
    		{
    			fgets(players[i].name, 20, stdin);
    			//scanf的返回值为正确接收变量的个数,若接收DQ,因为不是scanf要求的整型数据,scanf返回值为0
    			//有关讲解: http://blog.csdn.net/linuxxulin/article/details/7018321
    			if (!strchr(players[i].name, '*')) players[i].is_pro = true;
    			for (int j = 0; j < 4; j++)
    			{
    				int flag = 0;
    				if (!scanf("%d", &players[i].sco[j]))
    				{
    					players[i].sco[j] = DQ;
    					flag = 1;
    				}
    				if (j < 2) players[i].all2 += players[i].sco[j];
    				players[i].all4 += players[i].sco[j];
    				if (flag) break;
    			}
    			char temp[15];
    			fgets(temp, 10, stdin);
    			//这是为了接收空白的一行,因为题目有:This line is followed by a blank line, and there is also a blank line between two consecutive inputs.
    		}
    		sort(players, players + n, cmp1);
    		int pos = 0, pos2 = 0;
    		while (pos < 70 ) pos++; //题目给定至少70人晋级
    		while (pos < n && players[pos].all2 == players[pos - 1].all2) pos++;//找并列70名
    		
    		sort(players, players + pos, cmp2);
    		while (pos2 < pos && players[pos2].all4 < DQ) pos2++;//这个循环是找到犯规的人从哪开始
    		if (pos != pos2) sort(players + pos2, players + pos, cmp3);
    		//如果不相等,必定是三四轮出现了犯规的人,犯规的区间进行sort排序,并且重新写出一个排序标准
    		int rank = 1, cur = 0, pos3, cnt = 0;
    		
    		//统计总分相等的,得到相等区间 [cur, pos3)(该相等区间中,还需要排除掉不拿奖金的业余选手),相等区间内的人平分他们的奖金之和
    		while (cur < pos2)
    		{
    			int sum = 0;// 区间内的人数
    			double ave_per = 0;//区间内的人的平均奖金百分比
    			for (pos3 = cur; players[pos3].all4 == players[cur].all4; pos3++)
    			{
    				if (players[pos3].is_pro)
    				{
    					sum++; ave_per += per[cnt++];//cnt表示当前发到奖金比例的下标,注意如果为不得奖的业余选手,他的比例会被后面的得奖选手用掉
    				}
    			}
    			if (sum) ave_per /= sum;//得到区间平均奖金比例百分数(尚未/100,不是真正所占的百分比)
    			//注意sum是否为0的判断千万不要忘写,否则程序很可能异常
    			for (int i = cur; i < pos3; i++)
    			{
    				players[i].rk = rank;//相等区间所有人等级一致
    				if (players[i].is_pro && sum) players[i].money = ave_per * money_sum / 100.0 + eps;//加eps避免浮点误差
    				if (players[i].is_pro && sum > 1 && cnt - sum < 70) players[i].is_T = true;
    				
    				//如果还属于并列前70名可拿奖金的选手(业余不可拿),且该等级对应有至少两个可拿奖金的并列选手,则为它标号T,表示该名次有人并列
    				if (cnt - sum >= 70) players[i].is_pro = false;
    				//将比例数值大于70的那些运动员自动设为非职业运动员,这样输出可以直接判断是否为职业运动员来进行输出奖金,这样设置是因为,这两类运动员都是没有奖金的
    			}
    			int temp = pos3 - cur;
    			cur += temp;
    			rank += temp;//等级和区间起点都要更新
    		}
    			printf("Player Name          Place     RD1  RD2  RD3  RD4  TOTAL     Money Won
    ");
                printf("-----------------------------------------------------------------------
    ");	
    		for (int i = 0; i < pos; i++)//pos为晋级到后半段的人数
    		{
    			printf("%-21s",players[i].name);
    			int temp = 10;//temp是用来控制格式的,表示输完名次,以及输完可能存在的T以后,还需要输出几个空格
    			if (players[i].all4 < DQ) //没犯规的人才有名次
    			{
    				printf("%d",players[i].rk);
    				temp -= get_len(players[i].rk);
    			}
    			if (players[i].is_T)//并列则输出并列符号
    			{
    				printf("T");
    				temp--;
    			}
    			for (int i = 0; i < temp; i++)printf(" ");
    			temp = 4;
    			for (int j = 0; j < 4; j++)
    			{
    				if (players[i].sco[j] != DQ) printf("%-5d",players[i].sco[j]);
    				else
    				{
    					temp -= j;
    					break;
    				}
    				//temp在该轮循环中,是为了找到DQ是否出现,出现在哪一局的得分栏,并且输出连续空格串控制格式,以输出TOTAL栏的DQ	
    			}
    			if (temp == 4) temp = 0;
    			for (int i = 0; i < temp; i++) printf("     ");
    			if (temp)
    			{
    				printf("DQ
    ");
    				continue;
    			}
    			if (players[i].is_pro)
    			{
    				printf("%-10d",players[i].all4);
    				printf("$%9.2lf",players[i].money);
    			}
    			else printf("%d",players[i].all4);
                printf("
    ");		
    		}
    		if (T) printf("
    ");
    	}
    	return 0;
    }

    /*
      法二参考《入门经典》的代码,理解后手敲了一次
      
      收获:
      1. 宏定义用在循环中,简化代码,如:
      #define REP(i,n) for(int i = 0; i < (n); i++)
      需要包含头文件 #include<cassert>
    	  
      2. gets()和sscanf()的配合使用,从有空格的字符串中分离出需要的数据
      该处理方法也可见:
      http://blog.csdn.net/lujiandong1/article/details/41439849
      
      3. assert宏在测试时的使用(见入门经典P123 或 blog: http://www.cplusplus.com/reference/cassert/assert/)
      assert宏的用法:assert(表达式);
      作用:当表达式为真时无变化,当表达式为加时强制终止程序,并给出错误提示(在测试时经常使用)
      
      4. 用sprintf函数将一些数据按照一定的格式写到字符串中(尤其适合格式控制,例如不足几位补0,左对齐右对齐等等)
      http://www.cplusplus.com/reference/cstdio/sprintf/
      
      *******两种方法比较:
      刘汝佳前辈的代码比之法一,有个很大的改进之处,在于,法二的代码的结构体中,增加了两个元素
      dq(表示是否犯规),rnds(有违规时,用之记录哪局违规)
      
      加了两个数据以后,输出时会变得更方便...而对违规的记录,法一是用一个很大的数来代替违规时的分数,使之必然排到最后
      
    */

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    using namespace std;
    
    #define REP(i, n) for (int i = 0; i < (n); i++)
    
    const int maxn = 144;
    const int n_cut = 70;
    
    struct Player
    {
    	char name[50];
    	int amateur;
    	int sco[4];
    	int all2, all4, dq;
    	int rnds;
    } player[maxn];
    
    int n;
    double mon_all, p[n_cut];
    
    //cmp1功能:比完前两局晋级时,有违规的排不曾违规的人后面;对没违规的选手,总分低的人排总分高的人前面
    //cmp1的排序虽然还不完全,但已经足以将并列前70名提取出来,并将有违规的人排到没违规的人后面
    bool cmp1(const Player &a, const Player &b)
    {
    	if (a.all2 < 0 && b.all2 < 0) return false;
    	if (a.all2 < 0) return false;
    	if (b.all2 < 0) return true;
    	return a.all2 < b.all2;
    }
    
    bool cmp2 (const Player &a, const Player &b)
    {
    	if (a.dq && b.dq) //两选手都违规
    	{
    		if (a.rnds != b.rnds) return b.rnds < a.rnds;
    		if (a.all4 != b.all4) return a.all4 < b.all4;
    		return strcmp(a.name, b.name) < 0;
    	}
    	if (a.dq) return false;
    	if (b.dq) return true;  //一人违规
    	if (a.all4 != b.all4) return a.all4 < b.all4;
    	return strcmp(a.name, b.name) < 0;
    	//两人都没违规,先比较总分,再将名字按字典序排列
    }
    
    void solve()
    {
    	printf("Player Name          Place     RD1  RD2  RD3  RD4  TOTAL     Money Won
    ");
    	printf("-----------------------------------------------------------------------
    ");
    	
    	int i = 0, pos = 0;
    	while (i < n)
    	{
    		if (player[i].dq) //如果有犯规,则后面的比赛都无法参加,没有总分也没有奖金
    		{
    			printf("%s           ",player[i].name); //名字在输入时已经控制好了长度,所以不必在printf输出时进行格式控制
    		    REP(j,player[i].rnds) printf("%-5d", player[i].sco[j]);
    		    REP(j,4-player[i].rnds) printf("     ");
    		    printf("DQ
    ");
    		    i++;
    		    continue;
    		}
    		
    		int j = i;
    		int m = 0; //等级相同的专业选手数
    		bool is_divide = false;
    		double tot = 0.0; //平均百分比
    		
    		while (j < n && player[j].all4  == player[i].all4)
    		{
    			if (!player[j].amateur) //非业余选手才有奖金
    			{
    				m++;
    				if (pos < n_cut) //并列前70名才有奖金比例的累加,否则即使是专业选手,也没有奖金分 
    				{
    					is_divide = true; // is_divide表示当前名次有并列获奖者,需要平分奖金
    					tot += p[pos++]; //pos记录当前循环到哪个奖金比例,业余选手不占用比例
    				}
    				/*这里有个小细节要注意:
    				如果由于可并列的原因,70个奖金百分比已经分完了,但是还有并列前70的人没枚举,这时候就是只累加人数,不累加奖金比例了
    				因为凡是在并列前70的都能平分,不过奖金比例不会变多,相当于如果是最后一组并列70的人,有可能会有5人平分3人份的奖金百分比的情况
    				*/
    			}
    			j++;
    		}
    		
    		//打印下标在[i,j)范围内的选手信息,因为他们的等级相等,(若有奖金),奖金也相等,因而一并处理
    		int rank = i + 1;
    		double amount = mon_all * tot / m;
    		/*
    		注意此处,之所以不必检查m是否为0,是因为按照代码循环条件的控制,如果能进入外部循环,必定已经满足 i < n,而j又从i开始循环,因而m=0仅可能出现在一种情况:
    	
    		这个编号为i的运动员自己是业余的,并且没有与之并列的专业运动员,这种情况下, amount当然会是一个无效的值,但是这种情况下,也不满足 amountd的输出条件 if(!player[i].amateur && is_divide) ,也就是这个无效值并不会被输出
    	
    		因此,若将 double amount = mon_all * tot / m; 换为
    		double amount; if (m) amount = mon_all * tot / m;
    		也完全没问题,我试过仍能AC,因为amount无效的情况下,程序也确实没有试图输出它,所以看上去是不会有什么影响的,当然有没有隐患就是另一个问题了...
    		*/
    		
    		while (i < j)
    		{
    			printf("%s ", player[i].name);
    			char t[5];
    			sprintf(t, "%d%c", rank, m > 1 && is_divide && !player[i].amateur? 'T' : ' '); //并且只有专业选手参与平分奖金,业余选手仅排名不拿奖
    			printf("%-10s", t);
    			REP(e, 4) printf("%-5d", player[i].sco[e]);
    			
    			if (!player[i].amateur && is_divide)
    			{
    				printf("%-10d", player[i].all4);
    				printf("$%9.2lf
    ", amount / 100.0);
    			}
    			else 
    			printf("%d
    ", player[i].all4);
    			i++;
    		}
    	}
    }
    
    int main()
    {
    	int T;
    	char s[40];
    	
    	gets(s);
    	sscanf(s, "%d", &T);
    	//注意这题由于每组数据前,都有整行的空白行,所以格式的处理务必小心,尤其注意,不要因为回车符没有处理好,导致本来该读整个空白行的代码,变成了读长度为0的空串,最后导致 RE
    	//上面两行可以用下面两行代替,千万注意别忘了 getchar(),否则RE 
    //	scanf("%d",&T);
    //	getchar();	
    	while (T--)
    	{
    		gets(s); //读取整行空行
    		
    		gets(s);
    		sscanf(s, "%lf", &mon_all);
    		
    		//奖金比例 
    		REP(i, n_cut)
    		{
    			gets(s);
    			sscanf(s, "%lf", p + i);
    		} 
    		
    		//选手信息
    		gets(s);
    		sscanf(s, "%d", &n);
    		assert(n <= 144);
    		
    		REP(k, n)
    		{
    			gets(s);
    			strncpy(player[k].name, s, 20);
    			
    			player[k].name[20] = 0; //strcpy和strncpy的差异之一就是,strncpy用于复制时,是不会自动加上结束符的,需要自己加上
    			player[k].amateur = 0;
    			
    			if (strchr(player[k].name, '*')) player[k].amateur = 1;
    			
    			player[k].all2 = player[k].all4 = player[k].dq = 0;
    			memset(player[k].sco, -1, sizeof(player[k].sco));
    			
    			REP(i, 4)
    			{
    				char t[5];
    				REP(j, 3) t[j] = s[20 + i * 3 + j]; t[3] = '';
    				
    				if (!sscanf(t, "%d", &player[k].sco[i]))
    				{
    					player[k].rnds = i;
    					player[k].dq = -1;
    					if (i < 2) player[k].all2 = -1;
    					break;  //若某一回合成绩为dq,作处理如下,因为初始化为-1了,所以只需要在违规那局标记分数即可 
    				}
    				else
    				{
    					player[k].all4 += player[k].sco[i];
    					if (i < 2)
    					player[k].all2 += player[k].sco[i];
    				}
    			}
    		}
    		
    		//晋级初赛
    		sort(player, player+n, cmp1);
    		assert(player[n_cut - 1].all2 >= 0);
    		
    		for (int i = n_cut - 1; i < n; i++)
    		if (player[i].all2 != player[i + 1].all2)
    		{
    			n = i + 1;
    			break;
    		} //找和第70名并列的,n的意义从此变为初赛晋级人数
    		
    		sort(player, player+n, cmp2);
    		//cmp2是题目真正要求实现的排列方式
    		
    		solve();
    		
    		if (T) printf("
    ");
    	}
    	return 0;
    }
    /*
      edition3:
      下面的版本摘取了法二中值得学习的地方,但大部分格式控制并不采用法二中的"gets + sscanf",因为个人觉得这个比直接scanf更容易出错和弄混,不过也有很大可能是我对此还不够熟练...
    */

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    using namespace std;
    #define REP(i, n) for (int i = 0; i < (n); i++)
    //#define debug
    const int maxn = 144;
    const int n_cut = 70;
    
    struct Player
    {
    	char name[50];
    	int amateur;
    	int sco[4];
    	int all2, all4, dq;
    	int rnds;
    } player[maxn];
    
    int n;
    double mon_all, p[n_cut];
    
    bool cmp1(const Player &a, const Player &b)
    {
    	if (a.all2 < 0 && b.all2 < 0) return false;
    	if (a.all2 < 0) return false;
    	if (b.all2 < 0) return true;
    	return a.all2 < b.all2;
    }
    
    bool cmp2 (const Player &a, const Player &b)
    {
    	if (a.dq && b.dq) //两选手都违规
    	{
    		if (a.rnds != b.rnds) return b.rnds < a.rnds;
    		if (a.all4 != b.all4) return a.all4 < b.all4;
    		return strcmp(a.name, b.name) < 0;
    	}
    	if (a.dq) return false;
    	if (b.dq) return true;  //一人违规
    	if (a.all4 != b.all4) return a.all4 < b.all4;
    	return strcmp(a.name, b.name) < 0;
    	//两人都没违规,先比较总分,再将名字按字典序排列
    }
    
    void solve()
    {
    	printf("Player Name          Place     RD1  RD2  RD3  RD4  TOTAL     Money Won
    ");
    	printf("-----------------------------------------------------------------------
    ");
    	
    	int i = 0, pos = 0;
    	while (i < n)
    	{
    		if (player[i].dq) 
    		{
    			printf("%s           ",player[i].name); 
    		    REP(j,player[i].rnds) printf("%-5d", player[i].sco[j]);
    		    REP(j,4-player[i].rnds) printf("     ");
    		    printf("DQ
    ");
    		    i++;
    		    continue;
    		}
    		
    		int j = i;
    		int m = 0; //等级相同的专业选手数
    		bool is_divide = false;
    		double tot = 0.0; //平均百分比
    		
    		while (j < n && player[j].all4  == player[i].all4)
    		{
    			if (!player[j].amateur) 
    			{
    				m++;
    				if (pos < n_cut) 
    				{
    					is_divide = true;
    					tot += p[pos++]; 
    				}
    			}
    			j++;
    		}
    
    		int rank = i + 1;
    		double amount = mon_all * tot / m;
    
    		while (i < j)
    		{
    			printf("%s ", player[i].name);
    			char t[5];
    			sprintf(t, "%d%c", rank, m > 1 && is_divide && !player[i].amateur? 'T' : ' '); //并且只有专业选手参与平分奖金,业余选手仅排名不拿奖
    			printf("%-10s", t);
    			REP(e, 4) printf("%-5d", player[i].sco[e]);
    			
    			if (!player[i].amateur && is_divide)
    			{
    				printf("%-10d", player[i].all4);
    				printf("$%9.2lf
    ", amount / 100.0);
    			}
    			else 
    			printf("%d
    ", player[i].all4);
    			i++;
    		}
    	}
    }
    
    int main()
    {	
    	int T;
    	char s[40];
    	
    	scanf("%d",&T);
    
    	while (T--)
    	{
    		scanf("%lf", &mon_all);
    		
    		//奖金比例 
    		REP(i, n_cut) scanf("%lf", &p[i]);
    
    		scanf("%d", &n);
    		assert(n <= 144);
    		getchar();
    		
    		REP(k, n)
    		{
    			fgets(player[k].name, 21, stdin);
    			player[k].amateur = 0;
    			
    			if (strchr(player[k].name, '*')) player[k].amateur = 1;
    			
    			player[k].all2 = player[k].all4 = player[k].dq = 0;
    			memset(player[k].sco, -1, sizeof(player[k].sco));
    			
    			REP(i, 4)
    			{
    				if (!scanf("%d", &player[k].sco[i]))
    				{
    					player[k].rnds = i;
    					player[k].dq = -1;
    					if (i < 2) player[k].all2 = -1;
    					break;
    				}
    				else
    				{
    					player[k].all4 += player[k].sco[i];
    					if (i < 2)
    					player[k].all2 += player[k].sco[i];
    				}
    			}
    			char temp[15];
    			fgets(temp, 10, stdin);
    		}
    		
    		sort(player, player+n, cmp1);
    		assert(player[n_cut - 1].all2 >= 0);
    	
    		for (int i = n_cut - 1; i < n; i++)
    		if (player[i].all2 != player[i + 1].all2)
    		{
    			n = i + 1;
    			break;
    		} 
    		
    		sort(player, player+n, cmp2);
    		
    		solve();
    		
    		if (T) printf("
    ");
    
    	}
    	return 0;
    }





  • 相关阅读:
    Ubuntu 13.04 配置Cocos2d-x记录
    Ubuntu系列Crontab日记记录
    只是为了拾起一只笔,所以写了这些
    XhProf安装教程–详细教程
    检查.gitignore语法
    JavaScript设置右下角悬浮窗
    Codeforces Round #428 (Div. 2) B
    2017 多校5 hdu 6093 Rikka with Number
    cf 834 E. Ever-Hungry Krakozyabra
    codeforces 834 D. The Bakery
  • 原文地址:https://www.cnblogs.com/mofushaohua/p/7789442.html
Copyright © 2020-2023  润新知