poj 1160 Post Office

动态规划

1.先考虑在1到n的村长里面放一个邮局的最优解,那么就是把邮局放在最中间的那个村庄处最优(若村庄数为偶数则最中间的村庄有两个哪个都可以)

2.当放m个邮局的时候,其他每个邮局相当于有一个控制范围,范围内的村庄离该邮局最近,那么m个邮局会把n个村庄分为m块,我们就是dp每一块,令每一块的值最优,以及加上还没有处理的大块的最优解,则是我们要的答案

dp[i][j]表示从1号到i号村庄放j个邮局的最优解

方程:dp[i][j]=min{ dp[k-1][j-1]+s[k][i] }   s[k][i]表示在k号村庄到i号村庄放1个邮局的最优解,也就是放在最中间

记忆化搜索

/*
dp[i][j]=min{ dp[k-1][j-1]+s[k][i] }
*/
#include <cstdio>
#include <cstring>
#define INF 0x3f3f3f3f
#define N 310
#define M 35
#define ABS(a,b) a-b>0?a-b:b-a
#define min(a,b) a<b?a:b

int dp[N][M],s[N][N],v[N],n,m;

int sum(int a , int b)
{
    int ans=0,mid=(a+b)>>1;
    for(int i=a; i<=b; i++)
        ans+=ABS(v[mid],v[i]);
    return ans;
}

int dfs(int i ,int j)
{
    int mid,c,k;
    if(j==1)
    {
        dp[i][j]=0;
        mid=(1+i)>>1;
        for(c=1; c<=i; c++)
            dp[i][j]+=ABS(v[mid],v[c]);
        return dp[i][j];
    }
    if(dp[i][j]!=-1) return dp[i][j];

    dp[i][j]=INF;
    for(k=i; k>=j; k--) //当前分块的右边界
    {
        if(!s[k][i]) //还没有被计算过
            s[k][i]=sum(k,i);
        dp[k-1][j-1]=dfs(k-1,j-1);
        dp[i][j]=min(dp[i][j],dp[k-1][j-1]+s[k][i]);
    }
    return dp[i][j];
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1; i<=n; i++) scanf("%d",&v[i]);
        memset(dp,-1,sizeof(dp));
        memset(s,0,sizeof(s));
        dfs(n,m);
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

递推

/*
递推版本,应该按照j从小往大递推
dp[i][j]=min{ dp[k-1][j-1]+s[k][i] }
*/

#include <cstdio>
#include <cstring>
#define INF 0x3f3f3f3f
#define N 310
#define M 35
#define ABS(a,b) a-b>0?a-b:b-a
#define min(a,b) a<b?a:b

int dp[N][M],s[N][N],v[N],n,m;

int sum(int a ,int b)
{
    int ans=0,mid=(a+b)>>1;
    for(int i=a; i<=b; i++)
        ans+=ABS(v[mid],v[i]);
    return ans;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&v[i]);
        for(int i=1; i<=n; i++)
            for(int j=i; j<=n; j++)
                s[i][j]=sum(i,j);
        for(int i=1; i<=n; i++)
            dp[i][1]=s[1][i];

        for(int j=2; j<=m; j++)
            for(int i=j; i<=n; i++)
            {
                dp[i][j]=INF;
                for(int k=i; k>=j; k--)
                    dp[i][j]=min(dp[i][j],dp[k-1][j-1]+s[k][i]);
            }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/scau20110726/p/2946256.html