洛谷P2649——游戏预言

博主前两天在洛谷做了一道不错的题(题号在标题),现在分享一下自己对题目的看法。

题目

在这里插入图片描述
题目很简单,第一行输入人数 m 跟每个人的牌数 n,第二行输入John的牌的点数。要求输出John最少能赢的次数。

分析

怎么求John最少能赢的次数呢?最少赢的次数,即分析John出牌最差的情况。这里我们可以运用一种反向田忌赛马的思想切入这个问题。
在故事田忌赛马中,田忌用下等马对别人的上等马,再用自己较好的马对别人稍微差一点的马。而我们这里提到的反向田忌赛马的思想与这个类似。由题目可以知道,如果John要打出他手上的最大牌,只要有一个人打出的牌比他大,他这轮就没有获胜;而当John要打出的牌是全场最大的时候,别人就打出自己的最小牌从而保留自己的高点数牌。如果在总共为n轮的游戏中都以这种方式出牌,对John都是最不利的,再统计这种情况下John赢的次数,即可得到John最少赢的次数。
我们先做基本的输入如下

	int m;		//游戏人数m
	int n;		//每个人的纸牌数n
	scanf_s("%d", &m);
	scanf_s("%d", &n);
	int all_card[50];	//John的所有牌
	int i;
	for (i = 0; i < n; i++) {
   
		scanf_s("%d", &all_card[i]);
	}
核心算法

因为牌的点数从1到n*m,总共进行n轮游戏,不难得出,按照以上的思路只有牌的点数为n(m-1)+1到nm即有前n个大点数的牌的时候,John才有取胜的可能 (可以试想两种极端的情况:1.John的牌均小于n(m-1)+1显然他此时获胜次数为0;2.John的牌从n(m-1)+1到nm,此时John每次都获胜) 。所以我们第一步就是从John所有的手牌里选出点数为n(m-1)+1到nm的牌。

	int selected_card[50];
	int index1;		//index1记录经过选择后的牌的个数
	for (i = 0,index1=0; i < n; i++) {
   
		if (all_card[i] > (m - 1) * n) {
   
			selected_card[index1] = all_card[i];
			index1++;
		}
	}

完成牌的挑选之后,核心问题就是怎么实现所谓的反向田忌赛马。通过以上分析,我们知道当一部分点数n(m-1)+1到nm的牌在John手里的时候,另外一部分必然在别人的手里。在这里别人手里的大点我们同意用数组

	int othercard[50];

进行表示。下面就是怎么出牌了,联系田忌赛马,我们可以想到可以进行这样的操作:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值