田忌赛马之最弱马又克制最强马问题。

Posted hanny007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了田忌赛马之最弱马又克制最强马问题。相关的知识,希望对你有一定的参考价值。

问题很简单 就是A手里有n本秘籍,B手里有n本秘籍,要进行n场比试,用过的秘籍不能再用。n在1到1000的范围内,数字越小的秘籍越强而1却又被1000克制,求问如果A知道B的出手顺序,那么A最多能赢几场?


 

 

如果没有1被1000克制这个条件,这道题就是一个很明显的田忌赛马问题。

对于单纯的田忌赛马问题,我们在这里用贪心的思路去做(也可以用dp,但我不会- -。)

1首先对A、B 的所有秘籍排序。

2对B的最强者我们从A中循环

如果A的最强者能胜过B的最强者,那么就把这两本去掉,胜场加1。

如果A的最强者不能胜过B的最强者,那么就拿A的最弱者去顶掉B的最强者,实现代价最小。

 

由这么个思路贪心解决这个问题的话由于两者的所有秘籍都已经排序,我们可以用两个“箭头” 来标识两个数组中的A_begin B_begin A_end B_end 这种数据结构来实现这么一个过程,还挺好想的。

 


 

但是当加上一个1被1000克制这么个条件,很明显我们就得先处理掉1 和 1000这两个特殊情况。然后把问题简化为田忌赛马问题。

首先,我们只要去除一方的1和1000就好了,不需要把两方的1和1000都排除。

1、对于A的所有1000从B中找1抵消,胜场+1;

 

如果剩下了1000(说明B手中没有1了) 那么就用A的剩下的1000们去换取B的2—999中的最强者们。

这个过程很好实现,两边的begin和end指针都是一个一个挨着移动的,相当于整块整块的从边上削去了相同长度的数据块。

 

2、对于A的所有1,从B中找2—999的最强者们,抵消掉并且胜场+1。

 

如果剩下了1(说明B手中没有2—999了,只有1和1000) 那么就用A的1先去换取B的1,如果没有1了就换取1000。

这个其实不是很好理解,为什么剩下了1要先去换1后换1000?

因为我们对于1和1000的处理其实就是想排除A中这两个数并且获得最大利益,让这个过程变为A的2-999间的田忌赛马问题,那么对于A中的2-999如果我们想多赢,那么肯定是要排除最强对手1,上面的第一步是赢掉这些1而这一步就是平掉这些1。

这个过程之中B的begin不是挨着移动了,因为要先找B中的2-999,所以我的做法是要在移动的时候记录B中的1的数目1_num(便于平1),然后在处理A的1的时候先和1_num比,如果比1_num小,那么就不管了(反正A中只有2-999了必赢B剩下的1000们),如果比1_num大,再去换取B中的1000。

 

3、A的1和1000排除完毕并且利益最大化,进行田忌赛马。


 

 

#include<stdio.h>
#include<algorithm>

using namespace std;
const int maxn=1000+10;
int qko[maxn];
int hht[maxn];

//// --------------------------这个题贪心的时候田忌赛马如果最强的比不过对面最强的时候要用最弱的去比 那么这个东西用什么数据结构去实现好?------------------
int main()
{
int qkoend,hhtend;
int qkobegin,hhtbegin;

int n;
while(scanf("%d",&n)!=EOF)
{
int point=0;
qkoend=hhtend=n-1;
qkobegin=hhtbegin=0;
for(int i=0;i<n;i++)
{
scanf("%d",&qko[i]);
}
for(int i=0;i<n;i++)
{
scanf("%d",&hht[i]);
}

sort(qko,qko+n);
sort(hht,hht+n);
for(int i=qkobegin;i<=n-1;i++)
{
if(qko[i]==1000)
{

for(int j=hhtbegin;j<=hhtend;j++)
{
if(hht[j]==1) { point+=1; qkoend--; hhtbegin++; break;} //qko的1000赢了对手的1 qko的尾数-1 hht的初数+1


}

}

}





for(int m=qkoend, i=qkobegin;i<=m;i++)
{
if(qko[i]==1000)
{
qkoend--;
hhtbegin++;
} //抛弃qko手中剩下的1000们 则hht手中必无1 换取hht2—999的最强者
}







int num_1=0;
for(int i=hhtbegin;i<=hhtend;i++)
{
if(hht[i]==1)
{
num_1++;
hhtbegin++; //寻找 hht中第一个2—999的数的位置
} //这些被cut掉的1该怎么处理??????不能简单qkoend--那样不对
}






for(int i=qkobegin;i<=qkoend;i++)
{
if(qko[i]==1)
{

for(int j=hhtbegin;j<=hhtend;j++)
{
if(hht[j]<=999 && hht[j]>=2 ) { point+=1; qkobegin++; hhtbegin++;break;} //qko的1赢了对手的2-999中的强者们 qko的初数+1 hht的初数+1


}

}

}










for(int i=qkobegin;i<=qkoend;i++)
{
if(qko[i]==1)
{
if(num_1>0)
{
num_1--;
qkobegin++;
}

else
{
qkobegin++;
hhtbegin++;
}

} //抛弃qko手中的所有1 换取hht手中的1 或者1000

}







//qko绝对只剩下2-999了 和 hht的数据们田忌赛马

for(int j=hhtbegin;j<=hhtend;j++) //按照从强到弱遍历hht的秘籍
{


for(int i=qkobegin;i<=qkoend;i++)
{




if(qko[i]<hht[j] ) { point+=1; qkobegin++; hhtbegin++; break;} //qko的最强者打败了hht的最强者
else if(qko[i]>=hht[j]) //qko的最强者会输给或者平了hht的最强者 用qko的最弱者去和它比
{

if(qko[qkoend]>=hht[j]) { qkoend--;hhtbegin++; break;} //无论平或者负都是一样的其实

}
}
}


if(point>0) printf("%d\n",point);
else if(point==0) printf("0\n");
}
}

因为是在参加校赛的时候做到了这么一道题(虽然当时没做出来),所以qko是A,hht是B对应去看就好了。

另外,对于这种题目我感觉最大的难点就是要想到各种可能的情况确实是心细才能做出来,对于我这种粗心的人着实还是有些逻辑上的难度,如果谁有更加快捷方便的想法,欢迎在评论中补充。

以上是关于田忌赛马之最弱马又克制最强马问题。的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1650 赛马

田忌赛马问题

田忌赛马算法详解

田忌赛马

田忌赛马

洛谷P1650赛马与codevs 2181 田忌赛马