NOI导刊2008模拟9—赛马 解题报告
2012-10-12 19:02 kliner 阅读(276) 评论(0) 编辑 收藏 举报题目大意:田忌与齐威王赛马,现给出一个正整数N表示两人各有N匹马,接下来第2-3行,每行N个整数,第二行表示齐威王每匹马的速度,第三行表示田忌每匹马的速度。已知田忌赢一局得1块钱,平局不得钱,输一局失去1块钱,安排一个策略使田忌赢钱最多。(N>=5000)
输出:一行,一个整数,为田忌最多可赢的钱数。
思路:首先将齐威王与田忌的马均按速度从大到小排列,由于齐威王的出马顺序并不影响田忌赢的最多钱数,因此可以假设齐王从大到小开始出马。因此,田忌可有以下策略:
1.当自己最强的马都比不赢齐威王当前的马时,取自己最弱的马与之进行比赛,这是显然的。
2.当自己最强的马可以比过当前齐威王的马时,干掉它。这条性质可以利用剪切—粘贴法证明。
3.如果自己最强的马与齐威王当前的马战成平局,这时就有待讨论。
由上可知田忌总是拿自己(排序后)两头的马与齐王的马比赛。这个思路确定后,后面就好做了。
设f[i][j]表示总共进行了i场比赛,田忌出了j匹强马所能赢得的最大钱数。则有:
f[i][j]=max{f[i-1][j-1]+g(i,j),f[i-1][j]+g(i,N-(i-j)+1)},注意讨论边界条件。以下贴出代码:
#include <iostream> #include <fstream> using namespace std; ifstream fin("horse.in"); ofstream fout("horse.out"); int N; short int tianji[5001],qiwang[5001],f[5001][5001]; inline int kliner(int a,int b) { return a>=b?a:b; } int partion(short int *a,int start,int end) { int j=start-1,i=start,t=0; for(;i<=end;i++) { if(a[i]>=a[end]) { j++; t=a[j]; a[j]=a[i]; a[i]=t; } } return j; } int quicksort(short int *a,int start,int end) { if(start>=end) return 0; int j=partion(a,start,end); quicksort(a,start,j-1); quicksort(a,j+1,end); return 0; } int quan(int i,int j) { if(tianji[i]>qiwang[j]) return 1; else if(tianji[i]==qiwang[j]) return 0; else return -1; } int main() { fin>>N; int i=0,j=0; for(i=1;i<=N;i++) { fin>>qiwang[i]; } for(i=1;i<=N;i++) { fin>>tianji[i]; } quicksort(qiwang,1,N); quicksort(tianji,1,N); for(i=1;i<=N;i++) { for(j=0;j<=i;j++) { if(j==0) f[i][j]=f[i-1][j]+quan(N-i+j+1,i); else if(j==i) { f[i][j]=f[i-1][j-1]+quan(j,i); } else { f[i][j]=kliner(f[i-1][j]+quan(N-i+j+1,i),f[i-1][j-1]+quan(j,i)); } } } int ans=-9999999; for(i=0;i<=N;i++) { ans=kliner(ans,f[N][i]); } fout<<ans<<endl; return 0; }
对于一道动规题目来说,确定状态、阶段是非常重要的,而确定状态、阶段前对题目结构的分析,将是能否将此题完美答出的关键。