题目链接
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=93135#problem/B
题目大意
将一堆有重量的石头分成两组,使得两组石头总重量差额最小;等价于,从一堆有重量的石头中选出若干个,使得这些石头的重量最接近总重量的一半。
思路
暴力穷举之DFS
每个石头的状态是取或不取,最多20个石头,即2^20个状态;
存在隐式图:(类似于week 1,problem D)
从第一个石头开始构图,左边表示不取,有边表示取,值表示当前所取石头的总重量;第二个石头在第一个的两个选择下又分别有两个选择;第N个石头在第N-1个石头的各个选择下分别有取或不取两个选择;叶子节点的值即为可能的结果。
算法步骤
1. 读入数据,脑海里构图(完全二叉树);
2. 从虚拟根节点(0,0)开始DFS;如果高度等于数据量(number),得到一个分组结果;否则,递归DFS,如下图。
3. 输出结果。
算法复杂度
1. 每个石头均有取或不取两种选择,一共有N个石头,共有2^N个选择。时间复杂度为O(2^N)。
2. 空间复杂度O(N),储存数据的数据(每个石头的重量)。
提交状态
源代码
一维动态规划
典型的01背包问题
Number个物品(价值等于重量),背包容量为sum/2。
放第i个物品时:
1.背包剩余空间小于data[i],不放,背包重量不变。
2.否则,背包重量等于放与不放此物品的最大值。
算法步骤
1. 读入数据,存入data[]数组,并求出总质量的一半,即背包容量。
2. 开一个一维数组flag[],大小为背包容量加一,下标表示当前背包重量,值(0,1)若干个石头的组合可以使得背包达到这个重量,1表示可以,0表示不可以。初始化flag[0]=1;
3. 对于每一个石头,从背包容量开始,向0搜索。如果当前容量j>=data[i],flag为0,且flag[j – data[i] ]=1,则说明当前这个石头可以放进来。使得背包重量更大。
4. 石头遍历完,算法结束。得到flag[i]=1 的i的最大值,输出sum-2*i即可。
算法复杂度
1. 输入数据O(n);
2. 找出各种可能组合O(n*sum/2)。
3. 时间复杂度为O(n*sum/2)。
4. 空间复杂度体现在DP数组flag上,大小为sum/2+1;复杂度为O(sum/2);
源代码
dp
/*
典型的01背包问题
背包容量为总重量的一半
放进背包为A组,未放进为B组
结果 = 总重量减去A组两倍
*/
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char const *argv[])
{
int number,sum=0,mid,ans=0;
cin>>number;
std::vector<int> data(number),flags;
for (int i = 0; i < number; ++i)
{
cin>>data[i];
sum+=data[i];
}
mid=sum/2;
flags.resize(mid+1,0); flags[0]=1;
for(int i=0;i<number;i++)
for(int j=mid;j>=data[i];j--) // 必须从最大开始
if(!flags[j])
{
flags[j]=1;
ans=max(ans,j);
}
cout<<sum-ans-ans<<endl;
return 0;
}
dfs
/*
暴力搜索 2^20 存在一个隐式图 类似于上周 1212 的广搜
*/
#include <iostream>
#include <stdlib.h>
#include <vector>
using namespace std;
std::vector<int> data;
int sum=0,number,ans=1e9;
void DFS(int high, int weight)
{
if(high==number)
{
ans = min(ans,abs((sum - weight) - weight));
return;
}
DFS(high+1,weight);
DFS(high+1,weight+data[high+1]);
}
int main(int argc, char const *argv[])
{
cin>>number;
data.resize(number);
for(int i=0;i<number;i++)
{
cin>>data[i];
sum+=data[i];
}
DFS(0,0);
cout<<ans<<endl;
return 0;
}