题目:http://poj.org/problem?id=3186
初学动态规划的很多人一开始都会将动态规划写成函数递归的形式,有点是这样很好理解为什么要这么写,然鹅缺点就是几乎所有数据稍微大点的动态规划题目都不能用这种方法做,很容易runtime error
最近重新看回3186这道题,忽然发现自己好像有一点会写while下的动态规划了,拿出来分享一下233
(2个月前在ACM集训营做到心态爆炸)
首先你需要写出一条动态规划方程
dp[i][j]:卖出物品i到物品j所能得到的最多的钱
dp[i][j]=max(time*num[i]+dp[i+1][j],time*num[j]+dp[i][j-1]) 其中num[i]为第i件物品的价值,time为此时的时间
这是第一步
假如是写成函数形式,思路就是从左到右,从dp[i][j] 到 dp[i+1][j] dp[i][j-1]你会发现这其实就是我们写方程的时候的思路,所以很好理解
(2个月前的代码,莫得注释)
但是对于while,由于是一个“线性”的式子,程序不能像调用函数一样,未知->已知->带回未知,while 无法回溯,因此只能已知->未知。
放这里就要想一下我们已经知道的是什么,读者可以想一下。
将式子从右到左看,从已知为起点。没错,我们只知道dp[i][i]。
因此改写顺序(时间从后往前)
(建议直接看后面的代码。。)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2005;
int dp[maxn][maxn],num[maxn];
int main(){
int N; cin>>N;
memset(dp,0,sizeof(dp));
for(int i=1;i<=N;i++) scanf("%d",&num[i]);
for(int i=1;i<=N;i++) dp[i][i]=N*num[i];
for(int dis=1;dis<N;dis++){ //i 为起点的距离
int time=N-dis,end=N-dis;
for(int i=1;i<=end;i++){
int j=i+dis;
dp[i][j]=max(dp[i][j],dp[i+1][j]+num[i]*time);
dp[i][j]=max(dp[i][j],dp[i][j-1]+num[j]*time);
}
}
cout<<dp[1][N]<<endl;
return 0;
}
(表达能力还是十分有限,感觉仍然没能说清楚 orz。。可能我自己也是不怎么会吧)