田忌赛马 题解

题目大意

想必大家都做过一道经典的贪心问题:田忌赛马。
本题的背景与田忌赛马大致相似:你和对手各有n匹马,每匹马都有一个能力值,你和对手每轮选择一匹自己的未上场的马进行比赛,当你的马的能力值大于对方时,你获得这轮的胜利。
你已知对手每匹马的出场顺序,如何安排你的马的出场顺序,使得自己的胜场最多?
与传统的贪心题不同的地方是,这次你希望在胜场最多的情况下,给出字典序最大的出场顺序(能力值的字典序)。

解题思路

首先,我们要对两个数组排序(b数组的顺序无所谓,不过a数组得存在另一个数组里排序,毕竟a的顺序是很有用的)
接着,我们要运用贪心的方法求出最多的胜场。
然后,我们for循环枚举当前PK的马,随后贪心求解
我们贪心的思想就是让小的去对掉大的,但是我们的代码中并没有明显的这一步。
因为如果当前没有任何一匹马能战胜这匹马,我们的判断就会起作用:如果这匹马不在最大成功匹配中,tot就不会减1
这样就会符合胜场最多的条件,同时字典序最大。

代码

#include<bits/stdc++.h>
using namespace std;
int n,k1,k2,k3,tot,a[5050],b[5050],c[5050],bb[5050],cc[5050],v[5050];
bool cmp(int x,int y){return x>y;}
int main()
{
//	freopen("horse.in","r",stdin);
//	freopen("horse.out","w",stdout);
	scanf("%d",&n); 
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		c[i]=a[i];
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	sort(c+1,c+n+1,cmp);//从大到小排列敌方的马的能力值 
	sort(b+1,b+n+1,cmp);//从大到小排列你的马的能力值 
	for(int i=1,j=1;i<=n&&j<=n;j++)//贪心求出最大赢的场数,用tot记录 
		if(b[i]>c[j])i++,tot++;
	for(int i=1;i<=n;i++)//枚举PK第几头马 
	{
		memset(v,0,sizeof(v));
		k2=k3=0;//清空 
		//k2:是否赢了敌方的马 
		//k3统计的是当前最大的成功匹配数 
		for(int j=1;j<=n;j++)
			if(c[j]==a[i]&&!cc[j]){cc[j]=1;break;}
		//cc[j]=1表示c[j]在前i个a中出现过 
		//v[j]表示最大的成功中有没有第j个数 
		for(int j=n,k=n;j&&k;j--)
		{
			if(bb[j])continue;//这个位有没有用过 
			while(k&&cc[k])k--;//前面出现过c[j],直接跳过 
			if(k&&b[j]>c[k])k--,v[j]=1,k3++;
		}
		bool f=1;
		//f表示的应该是是否要去掉当前这个匹配 
		for(int j=1;j<=n;j++)//贪心从大到小枚举 
		{
			if(!bb[j])//没有被用过 
			{
				if(b[j]>a[i])k2=1;//可以杀死敌方的马 
				if(!v[j])f=0;//没有在最大成功匹配中,不用删掉 
				if(k2+k3==tot+f)//选择这个数后面的答案是否正确 
				{
					printf("%d ",b[j]);//输出答案 
					bb[j]=1;//标记成已使用 
					tot-=k2;//让tot减去这轮的情况 
					break;//直接跳出循环 
				}
			}
		}
	}
	return 0;
}
上一篇:【网络流】P1791 [国家集训队]人员雇佣


下一篇:P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】