最近觉得动态规划真的很练脑子,对建模以及思维方法有很大帮助,线段树被卡到有点起不来的感觉
最近仔细思考了一下动态规划的思想,无非是由局部最优解得到全局最优解,由此类推,发现,像最短路和最小生成树其实都是动态规划的思想在里面。
这题Chopstick,在建立状态 和 怎样递推 是两个难点。
在建立状态方面,通过dp[i][j],n-i+1为可选的筷子数,j为已选筷子组数,所以最终要求的结果自然是 dp[1][k].
递推方面,要注意两个细节,第一,每一组的那两根短筷子,必定是相邻的,可以反证法证明。第二,那根长筷子,如果采取逆推方式,就可以不用管了,因为逆推只要保证前面有长筷子即可。。。
for(i=n-2;i>=1;i--)
for (j=1;j<=k;j++)
dp[i][j]=min(dp[i+1][j],dp[i+2][j-1]+badness) (n-i+1>3*j)(即有筷子可选。)(关于为什么是i+2,我想了有一会儿,后来想通了,因为最长的那根筷子不一定要是跟这两根短的是连续的,只要序列前有长筷子即可,故只要往前找两根前的状态即可)。
dp[i][j]=dp[i+2][j-1]+badness(n-j+1==3*j) 这个时候筷子数刚好满足要挑选的筷子组数,因此无其他筷子可选,只有就近两支可以、。。
#include <iostream> #include <cstdio> using namespace std; int min(int a,int b) { if (a<b) return a; return b; } int dp[5005][1050]; int a[5005]; int main() { int i,j; int t,n,k; scanf("%d",&t); while (t--) { scanf("%d%d",&k,&n); k+=8; for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) { dp[i][0]=0; } for (i=n;i>=n-1;i--) { for (j=1;j<=k;j++) dp[i][j]=200000000; } for (i=n-2;i>=1;i--) { for (j=1;j<=k&&n-i+1>=3*j;j++) { if (n-i+1>3*j) { dp[i][j]=min(dp[i+1][j],dp[i+2][j-1]+(a[i+1]-a[i])*(a[i+1]-a[i])); } else dp[i][j]=dp[i+2][j-1]+(a[i+1]-a[i])*(a[i+1]-a[i]); } } printf("%d ",dp[1][k]); } return 0; }