题干:
https://www.luogu.org/problemnew/show/U43391
自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包。 小A的背包最多能装W的价值
现有n件物品,分别为v1,v2,v3……vn
问如何存放物品,使背包内物品总价值达到最大
输入输出格式
输入格式:
输入第一行包含两个整数,分别代表n和W。 以后N行,每行一个正整数vi 表示第i个物品的价值
输出格式:
输出仅一个整数,即最大价值。
输入输出样例
输入样例#1:
5 10 1 3 4 8 9
输出样例#1:
10
说明
1 <= n <= 40
1 <= vi <= 1e7
1 <= w <= 1e9
解题报告:
这题显然是背包问题,但是就是没法用背包做。直接暴力加点剪枝就可以过了,而且跑的很快,毕竟数据水、、并且题目说了是暴力AC。
但是正解是什么呢?很玄学、、把n从中间分成两份,然后答案就是三种情况:要么都在第一份,要么都在第二份,要么在第一份和第二份中各一个。很类似那道【CodeForces - 799C】Fountains 的做法。这里给出syt学长的代码。
暴力代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[55];
int n,m,loc;
ll ans = 0;
void dfs(int x,ll cur) {
if(x == loc) {
ans = max(ans,cur);
return;
}
ans = max(ans,cur);
for(int i = x+1; i<=loc; i++) {
if(cur+a[i] <= m)
dfs(i,cur+a[i]);
}
}
int main()
{
cin>>n>>m;
for(int i = 1; i<=n; i++) scanf("%lld",a+i);
sort(a+1,a+n+1);
for(int i = 1; i<=n; i++) {
loc = i;
if(a[i] > m) break;
}
dfs(1,0);
if(a[1] <= m)
dfs(1,a[1]);
printf("%lld\n",ans);
return 0 ;
}
其中dfs函数也可以这么写:
void dfs(int x,ll cur) {//代表截止当前(x)位置,价值为cur的状态。
ans = max(ans,cur);
if(x == loc) return;
for(int i = x+1; i<=loc; i++) {
if(cur+a[i] <= m) dfs(i,cur+a[i]);
}
}
或者这样:
void dfs(int x,ll cur) {
if(x == loc) {
ans = max(ans,cur);
return;
}
if(cur+a[x+1] <= m)
dfs(x+1,cur+a[x+1]);
dfs(x+1,cur);
}
其实这些代码都是等价的,画一个搜索树就看出来了。但是刚开始写也会写崩、、反正还是没理清楚到底表示的是什么状态。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+10;
ll a[maxn],b[maxn];
ll v[50];
ll w;
int pre[50];
int n;
void init() {
int i;
pre[0]=1;
for(i=1; i<=30; i++) pre[i]=2ll*pre[i-1];//2的n次方
}
int main() {
ll t,sum,ans;
int i,j,k,l,r,m,p;
init();
scanf("%d%lld",&n,&w);
for(i=1; i<=n; i++) scanf("%lld",&v[i]);
ans=0;
for(i=0; i<pre[n/2]; i++) {
t=i,sum=0,j=n/2,k=1;
while(j>0) {
if(t%2) sum+=v[k];
t/=2,j--,k++;
}
a[i]=sum;
if(sum<=w) ans=max(ans,sum);
}
for(i=0; i<pre[n-n/2]; i++) {
t=i,sum=0,j=n-n/2,k=n/2+1;
while(j>0) {
if(t%2) sum+=v[k];
t/=2,j--,k++;
}
b[i]=sum;
if(sum<=w) ans=max(ans,sum);
}
sort(b,b+pre[n-n/2]);
/*
printf("*%d %d*\n",pre[n/2],pre[n-n/2]);
for(i=0;i<pre[n/2];i++) printf("%lld ",a[i]);
printf("\n");
for(i=0;i<pre[n-n/2];i++) printf("%lld ",b[i]);
printf("\n");
*/
for(i=0; i<pre[n/2]; i++) {
if(a[i]<w) {
l=0,r=pre[n-n/2]-1,p=-1;
m=(l+r+1)/2;
while(l<r) {
m=(l+r+1)/2;
if(a[i]+b[m]<=w) l=m;
else r=m-1;
}
// printf("l = %d\n",l);
if(l!=0) ans=max(ans,a[i]+b[l]);
}
}
// for(i=0; i<pre[n/2]; i++) {
// if(a[i]<w) {
// l=0,r=pre[n-n/2]-1,p=-1;
// while(l<=r) {
// m=(l+r)/2;
// if(a[i]+b[m]<=w) l=m+1,p=m;
// else r=m-1;
// }
// printf("p = %d\n",p);
// if(p!=-1) ans=max(ans,a[i]+b[p]);
// }
// }
printf("%lld\n",ans);
return 0;
}
骚操作:
#include<bits/stdc++.h>
#define Mit map<int,bool>::iterator
using namespace std;
typedef long long ll;
map<int,bool> M;
vector<int> V;
int main()
{
int n,w;
scanf("%d%d",&n,&w);
int x;
for(int i=0;i<n;i++){
scanf("%d",&x);
V.clear();
if(x<=w){
V.push_back(x);
for(Mit it=M.begin();it!=M.end()&&it->first<=w-x;it++){
V.push_back(it->first+x);
}
for(int j=0;j<V.size();j++){
M[V[j]]=true;
}
}
}
ll ans=0;
Mit it=M.upper_bound(w);
if(it!=M.begin()){
it--;
ans=it->first;
}
printf("%d\n",ans);
return 0;
}