【洛谷 - U43391】不是0-1背包的暴力AC(思维,二分,可转化为二元组问题,复习暴力dfs总结)
題干:
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; }?
總結
以上是生活随笔為你收集整理的【洛谷 - U43391】不是0-1背包的暴力AC(思维,二分,可转化为二元组问题,复习暴力dfs总结)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 德普3亿美元回归《加勒比海盗》?发言人:
- 下一篇: 【CodeForces - 298D】F