题干:
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;
}