NOI: Post Office

一、题目大意:

在这里插入图片描述

二、解题思路

  • 定义:
    • d p [ i ] [ j ] dp[i][j] dp[i][j]:前 i i i个村庄建 j j j个邮局的最小距离.
    • m [ i ] [ j ] m[i][j] m[i][j]:村庄 [ i , j ] [i,j] [i,j]之间建立一个邮局的最小距离.(想象: 很明显应该建在 ( i + j ) / 2 (i+j)/2 (i+j)/2的邮局上)
  • 目标状态: d p [ V ] [ P ] dp[V][P] dp[V][P].即前 V V V个村庄建立 P P P个邮局.
  • 状态转移:
    • 子问题:
      • 定义: b u i l d ( s , e , j ) : = build(s,e,j):= build(s,e,j):=在村庄 [ s , e ] [s,e] [s,e] 间建 j j j个邮局
      • b u i l d ( 1 , i , j ) : = ⋃ k = 1 i − 1 { b u i l d ( 1 , k , j − 1 ) ∩ b u i l d ( k + 1 , i , 1 ) } build(1,i,j):=\bigcup_{k=1}^{i-1} \{build(1,k , j-1)\cap build(k+1, i, 1)\} build(1,i,j):=k=1i1{build(1,k,j1)build(k+1,i,1)}
    • 因此有: 状态转移方程: d p [ i ] [ j ] = m a x { d p [ k ] [ j − 1 ] + m [ k + 1 ] [ i ] ∣ 1 &lt; = k &lt; = i − 1 } dp[i][j] = max\{dp[k][j-1]+m[k+1][i]\quad | \quad 1&lt;=k&lt;=i-1\} dp[i][j]=max{dp[k][j1]+m[k+1][i]1<=k<=i1}(这里默认了每一个子问题下,最优情况为: 前 k k k个村庄只往前 j − 1 j-1 j1个邮局前进,如果是要往第 j j j个邮局,这种情况包含在其他子问题下)因此状态转移最终结果没有问题。
    • m [ i ] [ j ] = m [ i ] [ j − 1 ] + a [ j ] − a [ ( i + j ) / 2 ] m[i][j] = m[i][j-1]+a[j]-a[(i+j)/2] m[i][j]=m[i][j1]+a[j]a[(i+j)/2](想象可得出)
  • 转移策略: 先求出所有的 m m m, d p [ i ] [ j ] dp[i][j] dp[i][j]只依赖与 d p [ i ′ ] [ j − 1 ] dp[i&#x27;][j-1] dp[i][j1],因此只需循环更新下三角矩阵。
  • 初始状态: d p [ i ] [ 1 ] = m [ 1 ] [ i ] , d p [ i ] [ 0 ] = i n f dp[i][1] = m[1][i], dp[i][0]=inf dp[i][1]=m[1][i]dp[i][0]=inf

三、 代码:

#include<iostream>
using namespace std;
const int MAXV = 304;
const int MAXP = 34;
const int inf = 1 << 30;
int dp[MAXV][MAXP];
int m[MAXV][MAXV];
int a[MAXV];
int main()
{
    int V, P;
    cin >> V >> P;
    for(int i=1; i<=V; i++)
        cin >> a[i];
    for(int i=0; i<=V; i++)
        for(int j=0; j<=P; j++)
            dp[i][j] = inf;
    for(int i=1; i<=V; i++)
        m[i][i] = 0;
    for(int i=1; i<=V; i++)
        for(int j=i+1; j<=V; j++)
            m[i][j] = m[i][j-1] + a[j] - a[(i+j)/2];
    for(int i=1; i<=V; i++)
        dp[i][1] = m[1][i];

    for(int i=2; i<=V; i++)
        for(int j=1; j<=i; j++)
            for(int k=1; k<=i-1; k++)
                dp[i][j] = min(dp[i][j], dp[k][j-1]+m[k+1][i]);

    cout << dp[V][P] << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值