
题目分析
田忌和齐王分别由n匹马,双方进行n轮比赛,每轮各派一匹马出场,每匹马只能使用一次 ,卖场数值大者拥有200银币,求田忌最多的获益。
求解思路
田忌赛马,中国人都知道的智慧!
两个人的马从大到小排:
- 田忌最大的>齐王最大的,直接+200
- 田忌最大的<齐王最大的,浪费齐王的好马,用田忌最差的马对战齐王最好的。-200
- 田忌最大的=齐王最大的。这总不能打平把,想想办法。
最好的搁置,看看最差的
3.1 如果田忌最差的>齐王最差的,先+200再说
3.2 如果田忌最差的<=齐王最差的, 用田忌最差的去消耗齐王最好的,如果田忌最差的<齐王最好的,则 -200。
代码实现
四指针分别指向田忌的最好和最差,齐王最好和最差,通过指针的变换指定马。
cpp
#include<bits/stdc++.h>
using namespace std;
int tian[2009],qi[2009];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
int n,ans=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>tian[i];
for(int i=1;i<=n;i++) cin>>qi[i];
sort(tian+1,tian+n+1,cmp);
sort(qi+1,qi+n+1,cmp);
int t1=1,tn=n,q1=1,qn=n;
while(t1<=tn)
{
if(tian[t1]>qi[q1])
{
ans+=200;
t1++;
q1++;
}
else if(tian[t1]<qi[q1])
{
tn--;//用田最差的
q1++;//齐王最好的
ans-=200;
}
else
{
if(tian[tn]>qi[qn])
{
ans+=200;
tn--;
qn--;
}
else //让田最差的去耗齐王最好的
{
if(tian[tn]<qi[q1])
ans-=200;
tn--;
q1++;
}
}
}
cout<<ans;
return 0;
}
也可也用双端队列
cpp
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
int main() {
int n;
cin >> n;
deque<int> tian(n), king(n);
for(int i = 0; i < n; i++) cin >> tian[i];
for(int i = 0; i < n; i++) cin >> king[i];
// 从大到小排序
sort(tian.begin(), tian.end(), greater<int>());
sort(king.begin(), king.end(), greater<int>());
int ans = 0;
while(!tian.empty()) {
if(tian.front() > king.front()) {
// 田忌最快 > 齐王最快
ans += 200;
tian.pop_front();
king.pop_front();
}
else if(tian.front() < king.front()) {
// 田忌最快 < 齐王最快
ans -= 200;
tian.pop_back(); // 用最慢的送死
king.pop_front(); // 消耗对方最快的
}
else {
// 田忌最快 == 齐王最快
if(tian.back() > king.back()) {
// 田忌最慢 > 齐王最慢
ans += 200;
tian.pop_back();
king.pop_back();
}
else {
// 田忌最慢 <= 齐王最慢
if(tian.back() < king.front())
ans -= 200;
tian.pop_back();
king.pop_front();
}
}
}
cout << ans << endl;
return 0;
}
我的bug
思路上的问题,最开始,当田忌最好的=齐王最好的时,先判断最差的,此时田忌的最差的马 ≤ 齐王最差的马,想法就直接用田忌最差的去消耗齐王最好的,直接-200,但是! 有没有可能田忌最差的也比齐王最好的强呢!

听起来匪夷所思,但也不是不可能,所以需要增加一个判断
当田忌最差的比齐王最好的差时,才会-200
cpp
else
{
if(tian[tn]>qi[qn])
{
ans+=200;
tn--;
qn--;
}
else //让田最差的去耗齐王最好的
{
//要判断一下,田最差的小于齐最好的才会-200!!!
if(tian[tn]<qi[q1])
ans-=200;
tn--;
q1++;
}
}
太严谨了!!!
其他思路
贪心匹配中的"尽量用刚好能赢的马去赢"策略
能赢则赢→ 不能赢就平→ 不行再输
· 阶段一:能赢则赢
从小到大枚举田忌的每一匹马ai,在齐王尚未出战的马中,寻找速度小于ai且最接近ai的马。
如果找到了,则进行比赛,田忌胜出。如果没找到,就先别急着让ai这匹马去送死,先跳过,继续处理ai+1。
这样能最大化地利用低速马换取胜利。
· 阶段二:不能赢就平
处理剩余的马对于田忌剩下的那些马,它们谁也打不过,最好的结果就是平局:
我们检查剩下的马中是否存在速度相等的,若相等则计为平局。
· 阶段三:不行再输
剩余的无法平局的马只能算作负场。
实现方法
使用multiset,将齐王的马全部存入std::multisetst。
先找胜场
遍历田忌排序后的马ai。
二分寻找齐王最后一个没出战且小于ai的马。
- 如果找到了,记录田忌胜利并从集合中删除齐王这匹马。
- 如果没找到,将ai存入等待队列。
再看平局
在上一个步骤中田忌剩余的马尝试与齐王集合中剩余的马匹配平局
最后平局
剩下的则全部计为负场
代码实现
cpp
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,ans=0;
cin>>n;
int a[n],b[n];
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) cin>>b[i];
sort(a, a+n);
multiset<int>st;
//齐王的马都放入multiset中
for(int x:b) st.insert(x);
vector<int> p; //存田忌的马中胜局没用的马
int ying=0,ping=0,shu=0;
for(int i=0;i<n;i++)
{
//对于a[i]找最大的小于a[i]的数
auto it=st.lower_bound(a[i]);//大于等于a[i]
if(it!=st.begin()) //找到了
{
--it;
ans+=200;
ying++;
st.erase(it);
}
else //没找到就将先先存下来
{
p.push_back(a[i]);
}
}
//对于田忌的马中胜局没用的马,看看能不能凑平局
for(int x:p)
{
auto it=st.find(x);
if(it!=st.end())//找到了
{
ping++;
st.erase(it);
}
}
shu=n-ying-ping;
//cout<<ping<<endl;
ans-=shu*200;
cout<<ans;
return 0;
}
我的bug......
疏忽

多次尝试发现

a数组固定长度或者长度为n都可以,
b如果是一个长度为 2009 的数组,但只有前 n 个元素有输入值,后面的都是随机值!
所以实际上,st 中包含了:n 个有效的齐王马的速度值,(2009-n) 个随机的垃圾值,会导致错误。
也可以写成上面完成代码的形式,或者用vector
