這道題我認爲很好,雖然難想。
題意是給你N個物品的重量,你每次搬兩個,搬K次,沒次搬的體力消耗是兩個物體重量的絕對值差的平方,問怎麼搬體力最小,輸出最小消耗的體力。
這道題我首先想到的是貪心,但是貪心顯然不好貪,我無法證明把物品從小到大排序後每相鄰的兩個的絕對值差是最小的。
所以我們得用動規的思想,想一想,要想解決搬K次物品體力消耗最小,那我第K-1次是不是也要體力消耗最小,同時,從K-1次轉移到K次怎麼轉移呢?
一下想不出,我也想了好久,還是先去看看數據吧,數據該怎麼處理呢,我要搬就會搬兩個物品,那麼我是不是改把物品兩兩合成爲選擇這一對所需要的花費呢?
顯然,我們對於排序後的物品重量知道,相鄰的一起拿會是最好的,因爲如果有a < b < c < d四個數,你會怎麼拿?
所以我們用a[i]來表示拿物品i和i+1的代價
這樣子我們是不是可以套用揹包裏面的套路就是
前K次搬運搬哪些物品花費最小
就是dp[前n個物品][k次搬運]
如果我們不拿第n個物品,那麼就是dp[前n-1個物品][k次搬運]
如果我們拿第n個物品的話,那麼就是dp[前n-2個物品][k-1次搬運]
顯然動規方程出來了,但是,問題還沒弄完,動態規劃的邊界問題還需要考慮。
如果我還沒搬就是k==0的情況,是不是0
如果k*2>n 就是搬過的次數大於現在的物品量,這顯然是不可能的,所有是一個非常大的數
OK
Talk is Cheap , Show me the Code 。
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <stack>
#define inf 1000000000
using namespace std;
int a[2010],dp[2020][2020];
int getnum(int x,int y)
{
if(y==0) return 0;
else if(2*y > x) return inf;
else return dp[x][y];
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(int i=1; i<n; i++)
{
a[i] = a[i+1] - a[i];
a[i] *= a[i];
}
memset(dp,0,sizeof(dp));
for(int i=1; i<=k; i++)
{
for(int j=i*2; j<=n; j++)
{
dp[j][i] = min(getnum(j-1,i),getnum(j-2,i-1) + a[j-1]);
}
}
printf("%d\n",dp[n][k]);
}
return 0;
}
寫完後表示對問題的分析又有一個深的見解。
路還很長,請堅持下去。