动 规 永 远 的 基 础 — — 01 背 包 问 题 动规永远的基础——01背包问题 动规永远的基础——01背包问题
题目大意:
你有N个石头,质量分别为W1,W2,W3…WN. (W<=100000) 现在需要你将石头分为两堆,使两堆质量的差为最小。
解题思路:
第一眼看到这道题,你会想起爆搜,它也确实能过,但是我们不那么做,为什么?
因为
搜索是粗暴的咆哮,而DP是自由与骄傲的吟唱
但是,这题怎么用DP做呢?状态是什么?阶段是什么?我们依然一无所知。
其实我们可以这么想,让两堆石头质量差最小,其实就是一个选与不选的过程,这是什么DP?是那个永远的神,拥有变化万千的算法——01背包DP啊!
那01背包到底是什么呢?我们来详细的聊聊……
再看看基础的01背包例题吧~~~
知道了可以用01背包来做,算法中的 物品价值 是什么也知道了,物品质量 是什么也知道了,但背包的容量到底多大啊?题目中的“使两堆质量差值最小”似乎没有明确给出啊!!
观察题目,我们知道了我们要求的是两堆石头的最小差值,那么最小差值的本质是什么?就是其中一堆石头减去另一堆石头的值,也就是说这题实际上就是让我们求其中的一堆石头该怎么安排——思路似乎变明朗了!
如何安排石头让差值最小呢?就是让其中的一堆石头的质量尽量接近所有石头的总质量之和的 1 2 \frac1 2 21!
有了这个思路,问题得以转换,让选取的变量尽量接近而不超过一个固定的常量,这不就是01背包算法吗?
设 V V V为所有石头的总质量之和。
石头有选与不选两种转态,有一个容量为 1 2 \frac1 2 21V的背包,如何在不超过容量的情况下装最重的石头
题解看到这里,你就会发现,这就是一道最普通的01背包题。
状态转移方程: d p v = m a x ( d p v , d p v − w i + a i ) dp_v=max(dp_v,dp_{v-w_i}+a_i) dpv=max(dpv,dpv−wi+ai)
也不多说了,重点是将问题层层转换为最原始的状态,只要明白了,任何算法都可以很简单
CODE
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int t=0,m,a[110],w[110],ans;
int dp[1000001];
void input()
{
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>w[i]; //单个石头质量
t+=w[i]; //用T表示石头总质量
}
}
void DP() //标准01背包 DP 代码
{
for(int i=1;i<=m;i++)
{
for(int j=t/2;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
ans=max(ans,dp[j]);
}
}
cout<<(t-ans)-ans; //输出两堆石头的差值
}
int main()
{
input();
DP();
return 0;
}
总结:
01背包 问题的最大特点就是多变。因此,01背包问题最大的难点,就是你能否将一个 畸形的01背包问题 转换为你所学过的,你所认识的那个样子,将一个最不像01背包的题目,用01背包的做法AC。
只要学会了题目之间的变通,01背包真的很简单。