1. 程式人生 > 實用技巧 >杭電多校第四場 1003 Contest of Rope Pulling(隨機化+動態規劃)

杭電多校第四場 1003 Contest of Rope Pulling(隨機化+動態規劃)

題意:

多組輸入,給定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]);
    }
}