1. 程式人生 > 實用技巧 >b_lg_郵局(暴力dp / 預處理優化 / 四邊形不等式)

b_lg_郵局(暴力dp / 預處理優化 / 四邊形不等式)

為了建立郵局,應選擇他們建造的位置,使每個村莊與其最近的郵局之間的距離總和最小。
你要編寫一個程式,已知村莊的位置和郵局的數量,計算每個村莊和最近的郵局之間所有距離的最小可能的總和。
輸出格式
第一行包含一個整數S,它是每個村莊與其最近的郵局之間的所有距離的總和。

方法一:dp

蒙的:一個郵局放在中間位置得到的距離總和最小;

  • 定義狀態
    • f[i][j] 表示在前i個村莊中建j個郵局的最小距離總和
  • 思考初始化:
    • f[...][...]=+inf, f[0][0]=0
  • 思考狀態轉移方程
    • f[i][j]=min(f[i][j], f[k][j-1]+dist(k+1, i)),看是更優:前i個村莊中建j個郵局的最小距離 | 在前k個村莊中建j-1個以及第j個郵局在k+1到i這些村莊中建立的距離
  • 思考輸出:f[V][P]

\(O(v^3 × p)\)tle之40/100...

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int V=3005, P=305, inf=0x3f3f3f3f;
int v,p,f[V][P],A[V];

int dist(int l, int r) {
    int m=l+(r-l)/2, ans=0;
    for (int i=l; i<m; i++) ans+=A[m]-A[i];
    for (int i=m+1; i<=r; i++) ans+=A[i]-A[m];
    return ans;
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>v>>p;
    for (int i=1; i<=v; i++) cin>>A[i];
    memset(f, inf, sizeof f);
    f[0][0]=0;

    for (int j=1; j<=p; j++)    //郵局
    for (int i=1; i<=v; i++)    //村莊
    for (int k=0; k<i; k++) {   //少量村莊
        f[i][j]=min(f[i][j], f[k][j-1]+dist(k+1, i));
    }   
    cout << f[v][p];
    return 0;
}

預處理每個村莊中心到其它點的距離到dist[i][j]中去代替函式dist(),枯了還是40/100...

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int V=3005, P=305, inf=0x3f3f3f3f;
int v,p,f[V][P],A[V],dist[V][V];

int initdist() {
    for (int i=1; i<v; i++)
    for (int j=i+1; j<=v; j++) {
        dist[i][j]=dist[i][j-1]+A[j]-A[i+j>>1];
    }
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>v>>p;
    for (int i=1; i<=v; i++) cin>>A[i];
    memset(f, inf, sizeof f); f[0][0]=0;
    initdist();

    for (int j=1; j<=p; j++)    //郵局
    for (int i=1; i<=v; i++)    //村莊
    for (int k=0; k<i; k++) {   //少量村莊
        f[i][j]=min(f[i][j], f[k][j-1]+dist[k+1][i]);
    }   
    cout << f[v][p];
    return 0;
}

複雜度分析

  • Time\(O(v^2×p)\)
  • Space\(O(vp)\)