P1650 [ICPC 2004 Shanghai R] 田忌赛马(同洛谷2587)

题目分析

田忌和齐王分别由n匹马,双方进行n轮比赛,每轮各派一匹马出场,每匹马只能使用一次 ,卖场数值大者拥有200银币,求田忌最多的获益。

求解思路

田忌赛马,中国人都知道的智慧!

两个人的马从大到小排:

  1. 田忌最大的>齐王最大的,直接+200
  2. 田忌最大的<齐王最大的,浪费齐王的好马,用田忌最差的马对战齐王最好的。-200
  3. 田忌最大的=齐王最大的。这总不能打平把,想想办法。
    最好的搁置,看看最差的
    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

相关推荐
朱一头zcy2 小时前
[牛客]BC38 变种水仙花
算法
努力学算法的蒟蒻2 小时前
day105(3.6)——leetcode面试经典150
算法·leetcode·面试
阿蒙Amon2 小时前
C#常用类库-详解Autofac
开发语言·c#
爱上妖精的尾巴2 小时前
8-18 WPS JS宏 正则表达式-边界匹配
开发语言·javascript·正则表达式·wps·jsa
格林威2 小时前
工业相机图像高速存储(C#版):内存映射文件方法,附堡盟相机C#实战代码!
开发语言·人工智能·数码相机·计算机视觉·c#·工业相机·堡盟相机
波波0072 小时前
每日一题:什么是强类型语言和弱类型语言?
开发语言
Ralph_Y2 小时前
正则表达式
开发语言·c++·正则表达式
Chan162 小时前
LeetCode 热题 100 | 矩阵
java·开发语言·数据结构·算法·spring·java-ee·intellij-idea
钓鱼的肝2 小时前
[GESP-4.2503.T2]二阶矩阵
c++·算法·矩阵