洛谷 P1650 田忌赛马(dp)
传送门
解题思路
本题从暴力dfs入手,推出dp转移方程。
先进行排序,速度越大编号越大。
假设齐王从最大开始的出马,dfs(now,l,r)表示齐王出到编号为now的马,田忌还剩下l-r之间的马(因为田忌一定是要么出最大的马,要么出最小的马)。
分类讨论,当田忌最大的马大于齐王,则一定拿下这场比赛;
当田忌最大的小于齐王,则一定演下这场比赛;
当两者最大的马相等,则两种都有可能,答案取max。
这样原始代码就出来了:
int dfs(int now,int l,int r){
if(now==0) return 0;
if(a[r]>b[now]) return 200+dfs(now-1,l,r-1);
if(a[r]<b[now]) return dfs(now-1,l+1,r)-200;
return max(dfs(now-1,l,r-1),dfs(now-1,l+1,r)-200);
}
再加个记忆化或者将其变成for循环,就变成了dp。
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=2005;
int n,a[maxn],b[maxn],dp[maxn][maxn];
template<class T>inline void read(T &x)
{
x=0;register char c=getchar();register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(f)x=-x;
}
int main(){
ios::sync_with_stdio(false);
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) read(b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int now=1;now<=n;now++){
for(int l=1;l<=n;l++){
int r=now+l-1;
if(a[r]>b[now]) dp[now][l]=200+dp[now-1][l];
else if(a[r]<b[now]) dp[now][l]=dp[now-1][l+1]-200;
else dp[now][l]=max(dp[now-1][l],dp[now-1][l+1]-200);
}
}
cout<<dp[n][1];
return 0;
}