杭電多校第四場 1003 Contest of Rope Pulling(隨機化+動態規劃)
阿新 • • 發佈:2020-07-31
題意:
多組輸入,給定a,b兩個陣列,長度分別為n,m。每個元素有兩個值:wi,vi,要求從兩個陣列中分別選出一個子陣列,使得兩個子陣列的wi和相同,並使總的vi之和最大。
資料範圍:n,m<=1e3,wi<=1e3,vi<=1e9,\(\sum(n+m)<=1e4\)
解法:
很容易想到分別對兩個陣列進行揹包,求出每種可能重量和的價值最大值,然後遍歷兩個dp陣列求出價值和的最大值,複雜度為\(n^2*wi\),但是這樣一組樣例就有1e9,5組就有5e9,穩TLE了。
首先想到將兩個揹包合併為一個,即對b陣列的wi取反加入到a陣列中,答案就是對a陣列做揹包後dp[0]的值。(由於陣列下標不能為負,需要對每個狀態加上一個base值,使所有狀態對應的下標為正)。
合併為一個揹包後,可以發現最後需要的答案就是一個dp[0](為方便描述暫且不加上base值),如果對所有可能的重量的狀態進行轉移,會浪費掉大量對答案不一定有貢獻的時間,那麼如何避免這種情況呢?應該採用對合並後的a陣列進行隨機化的方法,基於一個隨機化的陣列,絕對值很大的狀態再轉移回dp[0]的概率是很低的,因此可以不考慮絕對值很大的狀態,即減小dp陣列的範圍,這樣答案也是有很大的機率是正確的。
算一下時間複雜度,評測機能跑5s,就是5e8的計算量,由於總和有限制,對於n=1000,m=1000=這種資料最多有5組,因此一組資料計算量是1e8,揹包要做n+m次,每次計算量為5e4(但是這樣可能會wa,要開大一點開1e5,評測機能3s穩過)
程式碼:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF=1e15; const int maxn=2e3+5; const int maxdp=1e5+5; const int base=5e4; const int lim=1e5; pair<int,int> pi[maxn]; void debug(ll *a,int l,int r){ for(int i=l;i<=r;i++){ printf("%d ",a[i]); } puts(""); } ll w[maxn],v[maxn]; ll temp[maxdp]; ll dp[maxdp];//dp[i]:i-base重量的最大價值 void init(){ fill(dp,dp+maxdp,-INF); fill(temp,temp+maxdp,-INF); dp[base]=0; temp[base]=0; } int main () { srand(time(0)); int T; scanf("%d",&T); while(T--){ int n,m; scanf("%d%d",&n,&m); init(); for(int i=1;i<=n+m;i++){ scanf("%d%d",&pi[i].first,&pi[i].second); if(i>n){ pi[i].first=-pi[i].first; } } random_shuffle(pi+1,pi+1+n+m); for(int i=1;i<=n+m;i++){ w[i]=pi[i].first; v[i]=pi[i].second; } for(int i=1;i<=n+m;i++){ for(int j=min(lim,(int)lim+(int)w[i]);j-w[i]>=0;j--){ if(dp[j-w[i]]!=-INF){ temp[j]=max(dp[j],dp[j-w[i]]+v[i]); } } for(int j=0;j<=maxdp-1;j++){ dp[j]=temp[j]; } } printf("%lld\n",dp[base]); } }