校OJ 8597 石子划分问题(dp)

来源:互联网 发布:大数据安全技术 编辑:程序博客网 时间:2024/05/29 04:20

解题思路:

1,先将石子重量从小到大排序(从大到小也可以).2,假设f(n,m)表示:n个石头分成m份的最小费用. 特别的,有f(1,1)=0; f(n,1)=(an - a1)^2那么,除去最后一份石头的若干个,前面m-1份必定也是最优的分法.若最后一堆1个石头, f(n,m) = f(n-1,m-1)+0^2若最后一堆2个石头, f(n,m) = f(n-2,m-1)+(an - an-1)^2若最后一堆3个石头, f(n,m) = f(n-3,m-1)+(an - an-2)^2......最后一堆最多只能有n-m+1个石头,因为当最后一堆为n-m+1时,前面m-1堆已经是一个一份了.因此, f(n,m) = Min{ f(n-1,m-1)+0^2,  f(n-2,m-1)+(an - an-1)^2,  ...}例如:n=5, m=2a[1..5] = 1 3 4 8 9f(5,2)=Min{ f(4,1)+0; f(3,1)+1; f(2,1)+5^2 }=Min{49,10,29}=10这5个石头分2堆的最优分法:(1 3 4)(8 9)
递推公式:
f(n,m)= f(n-k,m-1)+(a[n]-a[n-k+1])^2,n-k>=m-1,k>0
注意处理边界问题,其中   f(n,n)=0,    f(n,1)=(a[n]-a[1])^2
例如求 n=5,m=3时,
 012345000000010(1,1)(2,1)(3,1)  20 (2,2)(3,2)(4,2) 30    (5,3)
#include <iostream>#include <cstring>#include <cmath>#include <algorithm>#include <cstdlib>#include <cstdio>using namespace std;#define N 1001#define INF 10000000int dp[N][N];int a[N];int n,m;int main(){    cin>>n>>m;    for(int i=1;i<=n;i++)    cin>>a[i];    sort(a+1,a+n+1);    //for(int i=1;i<=n;i++)    //cout<<a[i]<<" ";    memset(dp,0,sizeof(dp));    for(int i=1;i<=n;i++)        //处理边界    dp[1][i]=(a[i]-a[1])*(a[i]-a[1]);    for(int i=2;i<=m;i++)    {        for(int j=i+1;j<=n;j++)        {            int min=INF;            for(int k=1;k<=j-i+1;k++)            {                if(min>dp[i-1][j-k]+(a[j]-a[j-k+1])*(a[j]-a[j-k+1]))                   min=dp[i-1][j-k]+(a[j]-a[j-k+1])*(a[j]-a[j-k+1]);            }            dp[i][j]=min;        }    }    /*for(int i=0;i<=m;i++)    {        for(int j=0;j<=n;j++)        cout<<dp[i][j]<<" ";        cout<<endl;    }*/    cout<<dp[m][n]<<endl;    return 0;}


0 0