装箱问题
此题主要是考察深搜剪枝的能力
题解:
首先,这道题目如果直接深搜会发现有一个不确定的数,那就是 c c c,那么,我们可以用迭代加深或者二分答案,在这里,我使用二分答案来讲解
剪枝:
- 如果当前找到答案了,就直接return;
- 如果当前的最大可能值小于总和,return;
- 如果第i组和第i+1组的空间相同,continue;
- (玄学) 将数字从大到小排序(因为这样数字产生的影响更大)
最后说明一下dfs;
dfs(int step,int sum)表示第step个数字,当前的最大可能值为sum;
Ac Code :
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,l,r) for(int i=l;i<=r;i++)//宏定义
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N = 50,INF=1e9,mod=INF+7;
int a[N],b[N];
int n,c,mid,cnt;
bool flag = false;
void dfs(int step,int sum){
if(flag)return;//如果找到答案了
if(sum < cnt) return;//如果当前最大值比所有数字加起来还小
if(step > n){//满足了全部的数字
flag = true;
return;
}
rep(i,1,mid){//枚举当前a[step]数字放在哪一组
if(b[i] + a[step] <= c){//不超过c
b[i] += a[step];
if(c-b[i] < a[n]) dfs(step+1,sum-c+b[i]);//如果这一组剩下的的空间小于最小的数,减去这些浪费的空间
else dfs(step+1,sum);
b[i] -= a[step];
}
while(b[i] == b[i+1])i++;//重复无需判断(没有此剪枝90分)
}
}
bool cmp(int a,int b){return a>b;}
int main()
{
int l=0,r,ans=INF;
cin >> n >> c;
rep(i,1,n){
cin >>a[i];
l += a[i];
cnt += a[i];
}
sort(a+1,a+1+n,cmp);//从大到小排序,通常更快
if(l % c == 0)l/=c;//至少需要ceil(l/c)组;
else l = l/c+1;
r = n;
while(l <= r){//二分答案(迭代搜索也可以通过)
mid = (l + r) >> 1;
flag = 0;//初始化,下同
fill(b,b+1+mid,0);//填充函数,将b至b+n填充为0
dfs(1,mid*c);//mid*c 是指此时状态的最大可能满足多大的数字
if(flag){
r = mid-1;
ans = min(ans,mid);
}else l = mid+1;
}
cout << ans <<endl;
return 0;
}