POJ3638 Moogle [DP]

来源:互联网 发布:化学分子式绘制软件 编辑:程序博客网 时间:2024/04/29 22:08

题意:

花了不少时间才读懂。

根据例子说吧,0 9 20 40 ,n=4,m=3,即可以存储3个,但是显然头尾0和40必须存储。

所以还可以存储一个,显然取20,这样可以根据公式退出i=2的这个位置的值是10,和9的误差为1,最后输出误差之和/n的值。

所以第二个例子:

10 4

0 10 19 30 40 90 140 190 202 210

最佳方案是取0 40 190 210 这样可以依照公式推出的序列为0 10 20 30 40 90 140 190 200 210,误差和为3。所以输出3/10=0.3


思路:


1<=i<=n;

1<=j<=m;

用dp [ i ][ j ]表示第 i 个元素放在第 j 个位置的误差。

dp[ i ][ j ]=inf; 初始值。

因为第一个元素和最后一个元素肯定要放在j=1,j=m的位置。

所以最后的输出答案值是dp[ n ][ m ]/n;

而dp [ 1 ][ 1 ]=0;(算是边界条件)


1<=i,j<=n;

用w [ i ][ j ]表示 区间[ i , j ]用公式算出来的误差和。

这个w数组很重要,有这个数组可以将整个算法时间复杂度从O(N^4)改进为O(N^3).


状态转移方程为:

dp[i][j]=min(dp[k][j-1]+w[k][j]) (1<=k<=i-1)

所以时间复杂度为O(N^3)

#include<iostream>#include<vector>#include<algorithm>#define Abs(a) ((a)>0?(a):-(a))#define Mod(a,b) (((a)-1+(b))%(b)+1)using namespace std;const int N=205;const double inf=100000000;int n,m;double dp[N][N];//double w[N][N];//int x[N];void solve(){memset(dp,0,sizeof(dp));memset(w,0,sizeof(w));for(int i=1;i<=n;i++)for(int j=i+2;j<=n;j++){for(int k=i+1;k<=j;k++){double tmp=x[i]+(x[j]-x[i])*(k-i)*1.0/(j-i);w[i][j]+=Abs(tmp-x[k]);}}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){dp[i][j]=inf;}dp[1][1]=0;for(int i=2;i<n;i++)for(int j=2;j<m&&j<=i;j++){for(int k=1;k<=i-1;k++){dp[i][j]=min(dp[i][j],w[k][i]+dp[k][j-1]);}}for(int k=1;k<=n-1;k++)dp[n][m]=min(dp[n][m],w[k][n]+dp[k][m-1]);printf("%.4lf\n",dp[n][m]/n);return;}int main(){int cases;scanf("%d",&cases);while(cases--){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",x+i);}solve();}return 0;}


原创粉丝点击