题目大意:田忌和齐王没人有n匹马,进行比赛,赢一场得200,看怎么安排能够让田忌赢的最多。
贪心算法。下面是某大神的分析与证明,本人转一下仅以记录。
算法可以用DP,或者给每匹马连线赋权变为二分图最佳匹配,还有就是贪心了。
1.当田忌最慢的马比齐王最慢的马快,赢一场先
2.当田忌最慢的马比齐王最慢的马慢,和齐王最快的马比,输一场
3.当田忌最快的马比齐王最快的马快时,赢一场先。
4.当田忌最快的马比齐王最快的马慢时,拿最慢的马和齐王最快的马比,输一场。
5.当田忌最快的马和齐王最快的马相等时,拿最慢的马来和齐王最快的马比.
田忌赛马贪心的正确性证明。
先说简单状况下的证明:
1.当田忌最慢的马比齐王最慢的马快,赢一场先。因为始终要赢齐王最慢的马,不如用最没用的马来赢它。
2.当田忌最慢的马比齐王最慢的马慢,和齐王最快的马比,输一场。因为田忌最慢的马始终要输的,不如用它来消耗齐王最有用的马。
3.当田忌最慢的和齐王最慢的马慢相等时,分4和5讨论。
4.当田忌最快的马比齐王最快的马快时,赢一场先。因为最快的马的用途就是来赢别人快的马,别人慢的马什么马都能赢。
5.当田忌最快的马比齐王最快的马慢时,拿最慢的马和齐王最快的马比,输一场,因为反正要输一场,不如拿最没用的马输。
6.当田忌最快的马和齐王最快的马相等时,这就要展开讨论了,贪心方法是,拿最慢的马来和齐王最快的马比.
前面的证明像公理样的,大家一看都能认同的,没有异议的,就不细说了。
证明:田忌最快的马和齐王最快的马相等时拿最慢的马来和齐王最快的马比有最优解。
1)假设他们有n匹马,看n=2的时候.
a1 a2
b1 b2
因为 田忌最快的马和齐王最快的马相等 所以a1=b1,a2=b2 所以这种情况有2种比赛方式,易得这两种方式得分相等。
2)当数列a和数列b全部相等等时(a1=b1,a2=b2...an=bn),显然最慢的马来和齐王最快的马比有最优解,可以赢n-1长,输1场,找不到更好的方法了。
3)当数列a和数列b元素全部相等时(a1=b1=a2=b2...=an=bn),无法赢也不输。
现在假设n匹马时拿最慢的马来和齐王最快的马比有最优解,证明有n+1匹马时拿最慢的马来和齐王最快的马比也有最优解。
数列
a1 a2 a3 a4...an an+1
b1 b2 b3 b4...bn bn+1
其中ai>=ai-1,bi>=bi-1
数列a和数列b不全部相等时,拿最慢的马来和齐王最快的马比数列得到数列
(a1) a2 a3 a4...an an+1
b1 b2 b3 b4...bn (bn+1)
分4种情况讨论
1.b1=b2,an=an+1
则有
a2 a3 a4...an
b2 b3 b4...bn
其中a2>=a1,a1=b1,b1=b2,得a2>=b2(此后这种大小关系不再论述),an>=bn.
此时若a2=b1,根据归纳假设,有最优解,否则a2>根据前面“公理”论证有最优解。
当且仅当a数列,b数列元素全部相等时有an+1=b1,已证得,所以an+1>b1,赢回最慢的马来和齐王最快的马比输的那一场。
2.b1<=b2,an=an+1
交换 b1,b2的位置,
数列
(a1) a2 a3 a4...an an+1
b2 b1 b3 b4...bn (bn+1)
此时 a2>=a1,an>=bn,
对于子表
a2 a3 a4...an
b1 b3 b4...bn
根据前面“公理”或归纳假设,有最优解。
an+1>=b2, 当且仅当b2=b3=b4=..=bn+1时有an+1=b2,这种情况,a中其它元素<=b1,b2,b3,b4..bn,对于这部分来说,能赢 x盘(x<=n),假如不拿最慢的马来和齐王最快的马比则拿最快的马来和齐王最快的马比,此时平一盘,能赢x-1盘,而拿最慢的马来和齐王最快的马 比,输一盘能赢x盘,总的来说,还是X这个数,没有亏。
3.b1=b2,an<=an+1
4.b1<=b2,an<=an+1证明方法类似,不再重复。
以证得当有n+1匹马的时候,田忌和齐王最快最慢的马速度相等时,拿最慢的马来和齐王最快的马比有最优解,已知当n=2时成立,所以对于n>2且为整数(废话,马的只数当然是整数)时也成立。当n=1时....这个似乎不用讨论.
代码中有我自己的注释,应该很明了:
#include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> using namespace std; int main() { int i,j,ans,tt,kk,n,t[1100],k[1100]; int cnt; while(scanf("%d",&n),n) { for(i=0;i<n;i++) scanf("%d",&t[i]); for(i=0;i<n;i++) scanf("%d",&k[i]); sort(t,t+n); //排序 sort(k,k+n); ans=0; i=j=0; // i 和 j 分别指向田忌和齐王最慢的马 tt=kk=n-1; // tt 和 kk 分别指向田忌和齐王最快的马 cnt=0; //用于计数,保证循环退出 while(cnt!=n) { if(t[i]>k[j]) // (一)田忌最慢的马比齐王最慢的马快的话,都用最慢的马,先赢一场 { ans++; i++;j++; } else if(t[i]<k[j]) // (二)田忌最慢的马比齐王最慢的马慢的话,分情况,需要看他们最快的马是什么情况 { if(t[tt]>k[kk]) // 1.这时如果田忌最快的马比齐王最快的马快的话,都用最快的马,赢一场 { ans++; tt--;kk--; } else if(t[tt]==k[kk]) // 2.如果他们的最快的马相等的话,看田忌最慢的马和齐王最快的马的情况(有两种,相等或比齐王的慢) { if(t[i]==k[kk]) // ①如果此时田忌最慢的马与齐王最快的马相等,平一场 { i++;kk--; } else // ②不然田忌最慢的马就比齐王最快的马要慢,输一场 { // 不会出现田忌最慢的马比齐王最快的马还快的情况 ans--; i++;kk--; } } else // 3.田忌最快的马比齐王最快的马慢,此时就是需要用田忌最慢的马消耗齐王最快的马,输一场 { ans--; i++;kk--; } } else if(t[i]==k[j]) // (三)如果他们最慢的马相等的话,也要去考虑他们最快的马的情况。。 { if(t[tt]>k[kk]) // 1.田忌的快,赢一场 { ans++; tt--;kk--; } else if(t[tt]<k[kk]) // 2.田忌的慢,此时就可以拿田忌最慢的马消耗齐王最快的马,输一场 { ans--; i++;kk--; } else if(t[tt]==k[kk]) // 3.此时最慢的马和最快的马都相等了,此时就要比田忌最慢的马和齐王最快的马 { if(t[i]<k[kk]) // ① 田忌最慢的马比齐王最快的马慢的话,输一场 { ans--; i++;kk--; } else if(t[i]==k[kk]) // ② 田忌最慢的马和齐王的马相等,平一场 { i++;kk--; } } } cnt++; } printf("%d\n",ans*200); } return 0; }