Loading

P4767 [IOI2000]邮局 二维四边形不等式

P4767 [IOI2000]邮局 二维四边形不等式

链接

二维的四边形不等式要比一维难证一些,事实上,在真正的考场上,一般都是打表找规律。

这个题的 dp 方程是 \(f_{i,j}=\min\limits_{i-1\le k\le j-1}\{f_{i-1,k}+w(k+1,j) \}\)

其中 \(f_{i,j}\) 为前 \(j\) 个村庄里设置 \(i\) 个邮局的最优解是多少。如果令 \(d_i\) 为村庄 \(i\) 的位置,\(sum_i\)\(\sum_{j=1}^id_j\) ,那么 \(w(i,j)=(2mid-i+1-j)d_{mid}+(sum_{i-1}+sum_j-2sum_{mid})\) ,其中 \(mid=\left\lfloor \frac{i+j}2 \right\rfloor\)

这里给大家提供一个暴力打表程序:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 10010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int V,P,d[N],sum[N],f[N][N],g[N][N];

inline int w(int i,int j){
    int mid=(i+j)>>1;
    return (2*mid-i+1-j)*d[mid]+(sum[i-1]+sum[j]-2*sum[mid]);
}

int main(){
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    read(V);read(P);
    for(int i=1;i<=V;i++) read(d[i]),sum[i]=sum[i-1]+d[i];
    memset(f,INF,sizeof(f));f[0][0]=0;
    for(int i=1;i<=P;i++){
        for(int j=i;j<=V;j++){
            for(int k=i-1;k<=j-1;k++){
                // f[i][j]=min(f[i][j],f[i-1][k]+w(k+1,j));
                if(f[i][j]>f[i-1][k]+w(k+1,j)){
                    f[i][j]=f[i-1][k]+w(k+1,j);
                    g[i][j]=k;
                }
            }
            printf("i:%d j:%d f:%d\n",i,j,f[i][j]);
            // printf("i:%d j:%d g:%d\n",i,j,g[i][j]);
        }
    }
    for(int i=1;i<=P;i++){
        for(int j=1;j<=V;j++) printf("%d ",g[i][j]);
        printf("\n");
    }
    printf("%d",f[P][V]);
    return 0;
}

正解代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 3010
#define M 310
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){
    return a<b?b:a;
}

ll V,P,d[N],sum[N],f[M][N],g[M][N];

inline int w(int i,int j){
    int mid=(i+j)>>1;
    return (2*mid-i+1-j)*d[mid]+(sum[i-1]+sum[j]-2*sum[mid]);
}

int main(){
    //freopen("my.in","r",stdin);
    //freopen("my.out","w",stdout);
    read(V);read(P);
    for(int i=1;i<=V;i++) read(d[i]),sum[i]=sum[i-1]+d[i];
    memset(f,INF,sizeof(f));f[0][0]=0;
    for(int i=1;i<=P;i++){
        g[i][V+1]=V-1;
        for(int j=V;j>=i;j--){
            for(int k=g[i-1][j];k<=g[i][j+1];k++){//g[i][j+1]
                // f[i][j]=min(f[i][j],f[i-1][k]+w(k+1,j));
                if(f[i][j]>f[i-1][k]+w(k+1,j)){
                    f[i][j]=f[i-1][k]+w(k+1,j);
                    g[i][j]=k;
                }
            }
            // printf("i:%d j:%d f:%d\n",i,j,f[i][j]);
            // printf("i:%d j:%d g:%d\n",i,j,g[i][j]);
        }
    }
    // for(int i=1;i<=P;i++){
    //     for(int j=1;j<=V;j++) printf("%d ",g[i][j]);
    //     printf("\n");
    // }
    printf("%lld",f[P][V]);
    return 0;
}

可以看出,正解和暴力没有什么太大区别,对于一维的决策单调性,虽然好证,但是打起来麻烦,二维的难证,但是打起来不麻烦,只需要注意一下是倒序枚举就可以了。

但是就实战而言,两者最好都打表证明。随意后者自然是更喜闻乐见的。

posted @ 2021-07-05 09:21  hyl天梦  阅读(58)  评论(0编辑  收藏  举报