1. 程式人生 > >51nod oj 1021 石子歸併【區間dp】

51nod oj 1021 石子歸併【區間dp】

題目連結:1021

每次只能合併相連的石堆-.-我們可以建一個dp

dp [ i ] [ k ] 表示從i號一共K個石頭合併再一起所花費的代價-.-

轉換方程1:dp[ i ] [ k ]=min( dp [ i ] [ k ] ,dp[ i ] [ j ] +dp [i+k ] [ k-j] +he[ i ] [ k ]);      j代表合併時第一個的長度--he[ i ] [ k ] 代表從i開始k個石頭的代價和

轉換方程1:dp[ i ] [ k ]=min( dp [ i ] [ k ] ,dp[ i ] [ j-i ] +dp [ j ] [ k-j+i] +he[ i ] [ k ] );   j代表合併時後一個的開頭--he[ i ] [ k ] 代表從i開始k個石頭的代價和

程式碼1:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[120][120],he[120][120],shu[120];
int main()
{
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            dp[i][j]=999999999;
    memset(he,0,sizeof(he));
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&shu[i]);
        dp[i][1]=0;
    }
    for (int k=2;k<=n;k++)
        for (int i=1;i+k-1<=n;i++)
            for (int j=i;j<i+k;j++)
                he[i][k]+=shu[j];
    for (int k=2;k<=n;k++)
        for (int i=1;i+k-1<=n;i++)
            for (int j=1;j<k;j++)
                dp[i][k]=min(dp[i][k],(dp[i][j]+dp[i+j][k-j])+he[i][k]);
    printf("%d\n",dp[1][n]);
    return 0;
}


程式碼2:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[120][120],he[120][120],shu[120];
int main()
{
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            dp[i][j]=999999999;
    memset(he,0,sizeof(he));
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&shu[i]);
        dp[i][1]=0;
    }
    for (int k=2;k<=n;k++)
        for (int i=1;i+k-1<=n;i++)
            for (int j=i;j<i+k;j++)
                he[i][k]+=shu[j];
    for (int k=2;k<=n;k++)
        for (int i=1;i+k-1<=n;i++)
            for (int j=i+1;j<i+k;j++)
                dp[i][k]=min(dp[i][k],(dp[i][j-i]+dp[j][k-j+i])+he[i][k]);
    printf("%d\n",dp[1][n]);
    return 0;
}