POJ1160 [IOI2000]邮局 [四边形不等式优化dp]

邮局

题目描述见链接 .


\color{red}{正解部分}

F[i,j]F[i, j] 表示前 ii 个村庄, 建立 jj 个邮局的最优值,

状态转移: F[i,j]=min0k<i(F[k,j1]+w(k+1,i))F[i, j] = \min_{0 \le k < i}(F[k, j-1] + w(k+1, i)),

其中 w(l,r)w(l, r) 表示在 [l,r][l,r] 放置一个邮局, 管辖 [l,r][l,r] 的村庄的最小值, 显然放在 中位数 位置最优,现在考虑如何 O(1)O(1) 计算 w(l,r)w(l, r),

sum_l[i]sum\_l[i] 表示 [1,i][1, i] 的距离前缀和, sum_r[i]sum\_r[i] 表示 [i,N][i, N] 的距离后缀和, 可以 O(N)O(N) 预处理 .

mm[l,r][l, r] 的中位数所在村庄编号,

w(l,r)=(ml)×A[m](sum_l[m1]sum_l[l1])+(sum_r[m+1]sum_r[r+1])(rm)×A[m]w(l, r) = (m-l)\times A[m] - (sum\_l[m-1] - sum\_l[l-1]) + (sum\_r[m+1]-sum\_r[r+1]) - (r-m)\times A[m]

然后经过 打表 可知, FF 满足四边形不等式: F[i+1,j+1]+F[i,j]F[i+1,j]+F[i,j+1]F[i+1,j+1]+F[i,j] \le F[i+1, j] + F[i, j+1]

同时与此对应的最优决策 p[i,j]p[i,j]满足决策单调性: p[i,j1]p[i,j]p[i+1,j]p[i,j-1] \le p[i, j] \le p[i+1, j].

于是可以使用 四边形不等式 优化 O(N2K)O(NK)O(N^2K) \rightarrow O(NK) ,


\color{red}{实现部分}

先给出 打表/暴力 程序

#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 3005;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N;
int K;
int A[maxn];
int p[maxn][maxn];

ll sl[maxn];
ll sr[maxn];
ll sum_l[maxn];
ll sum_r[maxn];
ll F[maxn][maxn];
ll w[maxn][maxn];

int main(){
        freopen("a.in", "r", stdin);
        freopen("a.out", "w", stdout);
        N = read(); K = read();
        for(reg int i = 1; i <= N; i ++) A[i] = read();
        std::sort(A+1, A+N+1);
        for(reg int i = 1; i <= N; i ++) sum_l[i] = sum_l[i-1] + A[i], sl[i] = sum_l[i] + sl[i-1];
        for(reg int i = N; i >= 1; i --) sum_r[i] = sum_r[i+1] + A[i], sr[i] = sum_r[i] + sr[i+1];
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = i; j <= N; j ++){
                        int m = i+j >> 1;
                        w[i][j] = (1ll*m-i)*A[m] - (sum_l[m-1] - sum_l[i-1]) + (sum_r[m+1] - sum_r[j+1]) - (1ll*j-m)*A[m];
                } 
        memset(F, 0x3f, sizeof F); F[0][0] = 0;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= i; j ++){
                        for(reg int k = 0; k < i; k ++){
                                ll t = F[k][j-1] + w[k+1][i];
                                if(t < F[i][j]){
                                        F[i][j] = t;
                                        p[i][j] = k;
                                }
                        }
                }
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= i; j ++)
                        if(i!=N && j != 1 && (p[i][j-1] > p[i][j] || p[i][j] > p[i+1][j])){
                                printf("False\n");
                        }else if(i != N && j != i && (F[i+1][j+1] + F[i][j] > F[i+1][j] + F[i][j+1])){
                                printf("False\n");
                        }
        printf("%lld\n", F[N][K]);
        return 0;
}

AcceptAccept codecode

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define reg register
typedef long long ll;

const int maxn = 3005;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N;
int K;
int A[maxn];
int p[maxn][305];

ll sum_l[maxn];
ll sum_r[maxn];
ll F[maxn][305];
ll w[maxn][maxn];

int main(){
        N = read(); K = read();
        for(reg int i = 1; i <= N; i ++) A[i] = read();
        std::sort(A+1, A+N+1);
        for(reg int i = 1; i <= N; i ++) sum_l[i] = sum_l[i-1] + A[i];
        for(reg int i = N; i >= 1; i --) sum_r[i] = sum_r[i+1] + A[i];
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = i; j <= N; j ++){
                        int m = i+j >> 1;
                        w[i][j] = (1ll*m-i)*A[m] - (sum_l[m-1] - sum_l[i-1]) + (sum_r[m+1] - sum_r[j+1]) - (1ll*j-m)*A[m];
                } 

        memset(F, 0x3f, sizeof F); F[0][0] = 0;

        for(reg int j = 1; j <= K; j ++){
                p[N+1][j] = N-1;
                for(reg int i = N; i >= 1; i --)
                        for(reg int k = p[i][j-1]; k <= p[i+1][j]; k ++){ 
                                ll t = F[k][j-1] + w[k+1][i];
                                if(t < F[i][j]) F[i][j] = t, p[i][j] = k; 
                        }
        }
        printf("%lld\n", F[N][K]);
        return 0;
}
posted @ 2019-09-19 20:11  XXX_Zbr  阅读(112)  评论(0编辑  收藏  举报