• 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) imes A[m] - (sum\_l[m-1] - sum\_l[l-1]) + (sum\_r[m+1]-sum\_r[r+1]) - (r-m) imes 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) ightarrow 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
    ");
                            }else if(i != N && j != i && (F[i+1][j+1] + F[i][j] > F[i+1][j] + F[i][j+1])){
                                    printf("False
    ");
                            }
            printf("%lld
    ", 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
    ", F[N][K]);
            return 0;
    }
    
  • 相关阅读:
    动手动脑2
    假期第五周学习记录
    假期第四周学习记录
    假期第三周学习记录
    假期第二周学习记录
    执行$JAVA_HOME/bin/java -version和java -version命令输出结果不一致
    假期第一周学习记录
    使用VMware创建虚拟机并下载linux系统镜像文件
    VMware Worstation下载安装
    第十七周学习进度
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822474.html
Copyright © 2020-2023  润新知