代码改变世界

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;
}


  对于一道动规题目来说,确定状态、阶段是非常重要的,而确定状态、阶段前对题目结构的分析,将是能否将此题完美答出的关键。