1. 程式人生 > 實用技巧 >P4799 [CEOI2015 Day2]世界冰球錦標賽

P4799 [CEOI2015 Day2]世界冰球錦標賽

\(0-1\)揹包解法

適用資料範圍:\(N≤40,M≤10^6\)
狀態表示:\(f(i,j)\):從前\(i\)個物品中選,花費不超過(小於等於)\(j\)的方案數。
最後累加\(f(n,0~m)\)即可。

const int N=45,M=1e6+10;
LL price[N];
LL f[M];
LL n,m;
LL ans;

int main()
{
    cin>>n>>m;

    for(int i=1;i<=n;i++) cin>>price[i];

    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=price[i];j--)
        {
            f[j]+=f[j-price[i]];
        }
    }

    for(int i=0;i<=m;i++) ans+=f[i];
    cout<<ans<<endl;

    //system("pause");
}

折半搜尋——正解

我們可以將所有比賽分成兩部分,對這兩部分分別進行指數型列舉,然後使用vector來儲存搜尋過程中產生的可能的花費。

最後再將兩部分進行合併,得出最終的答案。

時間複雜度:\(O(2^{\frac{n}{2}}log2^{\frac{n}{2}})\)

const int N=45;
LL price[N];
vector<LL> suml;
vector<LL> sumr;
LL n,m;
LL ans;

void dfs(int l,int r,LL sum,vector<LL> &v)
{
    if(l>r)
    {
        v.pb(sum);
        return;
    }
    if(sum + price[l] <= m) dfs(l+1,r,sum+price[l],v);
    dfs(l+1,r,sum,v);
}

int main()
{
    cin>>n>>m;

    for(int i=0;i<n;i++) cin>>price[i];

    dfs(0,n/2-1,0,suml);
    dfs(n/2,n-1,0,sumr);

    sort(suml.begin(),suml.end());

    for(int i=0;i<sumr.size();i++)
    {
        ans+=upper_bound(suml.begin(),suml.end(),m-sumr[i])-suml.begin();
    }

    cout<<ans<<endl;
    //system("pause");
}