算法分析:
这个问题很显然可以转化成一个二分图最佳匹配的问题。把田忌的马放左边,把齐王的马放右边。田忌的马A和齐王的B之间,如果田忌的马胜,则连一条权为200的边;如果平局,则连一条权为0的边;如果输,则连一条权为-200的边。 然而我们知道,二分图的最佳匹配算法的复杂度很高,无法满足N=2000的要求。 我们不妨用贪心思想来分析一下问题。因为田忌掌握有比赛的“主动权”,他总是根据齐王所出的马来分配自己的马,所以这里不妨认为齐王的出马顺序是按马的速度从高到低出的。由这样的假设,我们归纳出如下贪心策略:
1、如果田忌剩下的马中最强的马都赢不了齐王剩下的最强的马,那么应该用最差的一匹马去输给齐王最强的马。 2、如果田忌剩下的马中最强的马可以赢齐王剩下的最强的马,那就用这匹马去赢齐王剩下的最强的马。 3、如果田忌剩下的马中最强的马和齐王剩下的最强的马打平的话,可以选择打平或者用最差的马输掉比赛。
第一个贪心策略的证明: 此时田忌的所有马都赢不了齐王的马,所以无论用最慢马去输还是用最快的马去输都同样是输,而处于贪心的思想,我们应该保留相比之下更强的马,因此用最慢的马去输一定不会比用别的马去输来得劣,所以这是最优策略。 证毕。
第二个贪心策略的证明: 假设现在齐王剩下的最强的马是A,田忌剩下的最强的马是B,如果存在一种更优的比赛策略,让B的对手不是A,而使得田忌赢更多的钱的话,那么设此时A的对手是b,B的对手是a: 1、 若b>A,则有B>a,b>A。这个结果和B>A,b>a是相同的。 2、 若a<b≤A,则有B>a,b≤A。这个结果不如B>A,b>a来得优秀。 3、 若b≤a≤A,则有B>a,b≤A。这个结果和B>A,b≤a是相同的。 由此可知,交换各自对手后,一定不会使得结果变劣,那么假设是不成立的。 证毕。
第三个贪心策略的证明: 因为田忌最快的马也只是和齐王的马打平,那么田忌只能选择平或输,选择平的话,当然只能用最快的马去平了;选择输的话当时是用最慢的马去输来得值得,这和第一个贪心策略的思路是一样的。 证毕。
我们发现,第三个贪心策略出现了一个分支:打平或输掉。如果穷举所有的情况,算法的复杂度将比求二分图最佳匹配还要高;如果一概而论的选择让最强的马去打平比赛或者是让最差的马去输掉比赛,则存在反例: 光是打平的话,如果齐王马的速度分别是1 2 3,田忌马的速度也是1 2 3,每次选择打平的话,田忌一分钱也得不到,而如果选择先用速度为1的马输给速度为3的马的话,可以赢得200两黄金。 光是输掉的话,如果齐王马的速度分别是1 3,田忌马的速度分别是2 3,田忌一胜一负,仍然一分钱也拿不到。而如果先用速度为3的马去打平的话,可以赢得200两黄金。
虽然因为第三个贪心出现了分支,我们不能直接的按照这种方法来设计出一个完全贪心的方法,但是通过上述的三种贪心策略,我们可以发现,如果齐王的马是按速度排序之后,从高到低被派出的话,田忌一定是将他马按速度排序之后,从两头取马去和齐王的马比赛。有了这个信息之后,动态规划的模型也就出来了! 设f[i,j]表示齐王按从强到弱的顺序出马和田忌进行了i场比赛之后,从“头”取了j匹较强的马,从“尾”取了i-j匹较弱的马,所能够得到的最大盈利。 状态转移方程如下: f[i][j]=max(f[i-1][j]+g[n-(i-j)+1][i],f[i-1][j-1]+g[j][i]); 其中g[i,j]表示田忌的马和齐王的马分别按照由强到弱的顺序排序之后,田忌的第i匹马和齐王的第j匹马赛跑所能取得的盈利,胜为200,输为-200,平为0。 本题小结: 虽然本题存在直接贪心的方法,不过它可以作为一个例子告诉大家,合理的运用贪心策略,分析出问题的一些本质之后,一些看似不能用动态规划做的题目便可以巧妙的确立出状态,继而可以用动态规划来求解。
补充:对于状态转移方程
f[i][j]=max(f[i-1][j]+g[n-(i-j)+1][i],f[i-1][j-1]+g[j][i]);
还应该有两种特殊一点的情况
1 进行到第i场比赛的时候,从头取了0匹马,那么所有的马均为从尾取的, 则f[i][j]=f[i-1][j]+g[n-(i-j)+1][i];
其中n-(i-j)+1表示从尾取的第(i-j)匹马在整个排好顺序的马中是第几匹马
例如 共有8匹马, 进行到第4场比赛的时候,从头取了0匹马,表示要从尾取4匹马
1 2 3 4 5 6 7 8
即要取编号为5的这匹马,5=8-(4-0)+1; 那么f[4][0]=f[3][0]+g[8-(4-0)+1][4]
2 进行到第i场比赛的时候,从头取了i匹马,表明一直都是从头取的马 f[i][j]=f[i-1][j-1]+g[j][i];
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 19321 Accepted Submission(s): 5641
#include<stdio.h> #include<string.h> int tian[1005],king[1005],g[1005][1005],f[1005][1005]; void bubblesort(int a[],int n) { int i,j,t; for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { if(a[i]<a[j]) { t=a[i]; a[i]=a[j]; a[j]=t; } } } } int max(int a,int b) { if(a>b) return a; else return b; } int main() { int n,i,j,maxx; while(scanf("%d",&n)!=EOF&&n) //输入为0的 时候结束输入 { for(i=1;i<=n;i++) scanf("%d",&tian[i]); for(i=1;i<=n;i++) scanf("%d",&king[i]); memset(g,0,sizeof(g)); memset(f,0,sizeof(f)); bubblesort(king,n); bubblesort(tian,n); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(tian[i]>king[j]) g[i][j]=200; else if(tian[i]<king[j]) g[i][j]=-200; else g[i][j]=0; } } for(i=1;i<=n;i++) { for(j=0;j<=i;j++) { if(j==0) f[i][j]=f[i-1][j]+g[n-(i-j)+1][i]; else if(i==j) f[i][j]=f[i-1][j-1]+g[j][i]; else f[i][j]=max(f[i-1][j]+g[n-(i-j)+1][i],f[i-1][j-1]+g[j][i]); } } maxx=-200005; for(j=0;j<=n;j++) { if(f[n][j]>maxx) maxx=f[n][j]; } printf("%d\n",maxx); } }