题目大意
想必大家都做过一道经典的贪心问题:田忌赛马。
本题的背景与田忌赛马大致相似:你和对手各有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;
}