POJ 1160 区间DP + 平行四边形优化

本文探讨了在多个村庄中合理选址建立邮局的问题,通过区间动态规划算法找到使所有村庄到最近邮局的距离之和最小化的最优解,并介绍了平行四边形优化技巧。

区间DP + 平行四边形优化

题意:

​ 有n个村庄现在要建立m个邮局,问怎么建邮局才能使得村庄到最近的邮局距离和最小。输出距离和即可。

思路:

​ 一般的区间dp是从小区间到大区间,而此题在此之外还有一个限制是m个邮局。对于此类问题可以直接建立dp的时候加上限制条件:dp[i][j]=min(dp[k][j1]+one[k+1][i]) 定义dp含义为前i个村庄建立了j个邮局的最小距离和,那么在建立第j的时候可以枚举之前已经求出的区间,从j-1个邮局的前提下加上现在的1个邮局,one[i][j] 的含义是在区间i到j 的范围之中建立一个邮局,其实也就是中位数的位置,然后递推找出最小的值即可。

  • 未优化代码。
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 305;
const int INF = 0x3f3f3f3f;

int n,m;
int a[maxn];
int one[maxn][maxn];
int dp[maxn][maxn];

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n; i++) {
        scanf("%d",&a[i]);
    }
    for(int i = 1;i <= n; i++) {
        for(int j = i;j <= n; j++) {
            if(i == j) one[i][j] = 0;
            else one[i][j] = one[i][j-1] + a[j] - a[(i+j)/2];
        }
    }
    memset(dp,INF,sizeof(dp));
    for(int i = 1;i <= n; i++) dp[i][1] = one[1][i];
    for(int i = 2;i <= n; i++) {
        int End = min(m,i);
        for(int j = 2;j <= End; j++) {
            for(int k = 1;k <= i; k++) {
                if(dp[i][j] > dp[k][j-1] + one[k+1][i])
                    dp[i][j] = dp[k][j-1] + one[k+1][i];
            }
        }
    }
    printf("%d\n",dp[n][m]);
    return 0;
}
  • 可以用四边形优化

平行四边形的ss数组是优化的核心,但是它是建立在:m[i,j]=minm[i,k]+m[k,j](ikj) 条件下的。

ss[i,j1]ss[i,j]ss[i+1,j] 是最优解的优化,但是不知道ss[i+1][j] 如何算出,但是可以知道原本的上界i+1。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 305;
const int INF = 0x3f3f3f3f;

int n,m;
int a[maxn];
int one[maxn][maxn];
int dp[maxn][maxn];
int ss[maxn][maxn];

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n; i++) {
        scanf("%d",&a[i]);
    }
    for(int i = 1;i <= n; i++) {
        for(int j = i;j <= n; j++) {
            if(i == j) one[i][j] = 0;
            else one[i][j] = one[i][j-1] + a[j] - a[(i+j)/2];
        }
    }
    memset(dp,INF,sizeof(dp));
    memset(ss,0,sizeof(ss));
    for(int i = 1;i <= n; i++) dp[i][1] = one[1][i];
    for(int i = 2;i <= n; i++) {
        int End = min(m,i);
        for(int j = 2;j <= End; j++) {
            ss[i+1][j] = i+1;
            for(int k = ss[i][j-1];k <= ss[i+1][j]; k++) {
                if(dp[i][j] > dp[k][j-1] + one[k+1][i]) {
                    dp[i][j] = dp[k][j-1] + one[k+1][i];
                    ss[i][j] = k;
                }
            }
        }
    }
    printf("%d\n",dp[n][m]);
    return 0;
}
  • 还有一个版本是i和j的循环可以换一换
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define inf 0x3f3f3f3f
int dp[305][305];
int x[305],one[305][305]={0};
int K[305][305];
int main()
{
    int N,M,i,j,k;
   // freopen("in.txt","r",stdin);
    while(cin>>N>>M){
        for(i=1;i<=N;++i)  scanf("%d",&x[i]);
        memset(dp,inf,sizeof(dp));
        for(i=1;i<=N;++i)
            for(j=i;j<=N;++j)
        if(i==j) one[i][i]=0;
        else
            one[i][j]=one[i][j-1]+x[j]-x[(i+j)/2];
        for(i=1;i<=N;++i) dp[i][1]=one[1][i];
        for(i=1;i<=N;++i) K[i][i]=1;
        for(j=2;j<=M;++j)
        {
            for(i=1;i<=N;++i)
            {
                K[i+1][j]=i+1;
                if(j>i) continue;
                for(k=K[i][j-1];k<=K[i+1][j];++k)
                {
                   int s=dp[k][j-1]+one[k+1][i];
                   if(dp[i][j]>s) {
                        dp[i][j]=s;
                        K[i][j]=k;
                   }
                }
            }
        }
        printf("%d\n",dp[N][M]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值