SSL 1276 石子合并 (动态规划 01背包)

动 规 永 远 的 基 础 — — 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,dpvwi+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背包真的很简单。

猜你喜欢

转载自blog.csdn.net/SAI_2021/article/details/119796034