1. 程式人生 > >HDU 1227 dp距離和最小,中位數的應用

HDU 1227 dp距離和最小,中位數的應用



在n個商店中建m個倉庫,使各個商店到倉庫的路程之和最小,商店到哪個倉庫是有選擇的,
總之路程之和要最小!

思路:


從第i個商店到第j個商店建一個倉庫,這個倉庫所建的位置一定是dis[(i+j)/2],即建在它的中位數處
所以,這個增加值就是case[i][j]=abs(dis[k]-dis[(i+j)/2])(i<=k<=j);


我們要把它初始為一個儘可能大的數,要找dp[i][j],首先dp[i][j]=10000000(儘可能的大);然後找前一個狀態,dp[i-1][m]
為啥是m呢?因為,上一個狀態的倉庫數是一定的,肯定是比該狀態少1,但是商店數就是不確定的了,它最小是
i-1,最大是j-1,即m的範圍就是(i-1<=m<=j-1),找到上個狀態後,再加上一個增加值,這個增加值是從m+1
到j之間建一個倉庫所增加的距離,即case[m+1][j];該狀態是dp[i-1][m]+case[m+1][j];那麼dp[i][j]就是兩值得最小,每次m的改變就會將最小的存入dp[i][j],最後一次的更新,得到該狀態的最小值;
這樣,我們就找到了狀態轉移方程
dp[i][j]=MIN(dp[i-1][m]+case[m+1][j]),(i-1<=m<=j-1);

中位數證明:

從n個數中找出一個點使其他點到該點的距離之和最小

個人理解
中位數證明:
5個一維座標 a,b,c,d,e(a<=b<=c<=d<=e)
以a作點:b+c+d+e-4*a
以b作點:c+d+e-3*b+b-a=c+d+e-2*b-a
以c作點:d+e-b-a
以d做點:e+2*d-a-b-c

通過做差法比較大小可知以c點作該點距離和最小

當然也可以暴力求出某一段的距離和的最小值

心得:

做這道題時總是想著利用好 j 這個點即想成了j作為或不作為倉庫,這時還要標記前一個倉庫的位置很很是麻煩。

有時候也要利用j點之前的點進行dp的推導:此題就沒有隻是單純的利用j點進行。

#include<bits/stdc++.h>

using namespace std;

const int maxn=220;
const int INF=99999999;

int dis[maxn],dp[maxn][maxn],cost[maxn][maxn];

int main()
{

    //freopen("input.txt","r",stdin);
    int n,k;
    int cases=0;
    while(scanf("%d%d",&n,&k))
    {
        int i,j,m;
        if(n==0 && k==0)
            break;
        for(i=1; i<=n; i++)
            scanf("%d",&dis[i]);

        for(i=1; i<=n; i++)
            for(j=i; j<=n; j++)
            {
                cost[i][j]=0;
                for(m=i; m<=j; m++)
                    cost[i][j]+=abs(dis[m]-dis[(i+j)/2]);
            }
        for(i=1; i<=n; i++)
            dp[1][i]=cost[1][i];

        for(i=2; i<=k; i++)
            for(j=i; j<=n-k+i; j++)
            {
                dp[i][j]=INF;
                for(m=i-1; m<=j-1; m++)
                    dp[i][j]=min(dp[i][j],dp[i-1][m]+cost[m+1][j]);
            }
        printf("Chain %d\nTotal distance sum = %d\n\n",++cases,dp[k][n]);
    }
    return 0;
}