有時候,問題的規模比較大,外面無法列舉所有元素的組合,但能列舉一半或者一部分的組合。此時,將問題拆分成兩半或幾部分後分別列舉,再合併他們,這一方法往往非常有效。
舉個例子
例題:給定各有n個整數的四個數列a,b,c,d。要從每個數列中個取出1個數,使四個數的和為0,求出這樣組合的個數。當乙個組合中有多個相同的數字時,把他們當不同的數字看待。(poj 2785)
思路:從四個數列中選擇的話共有n4種情況,所以全部列舉一遍肯定不行,不過將他們對半分成ab和cd再考慮,就可以快速的解決了。從兩個數列中選擇的話就只有n2種組合,可以進行列舉。從a、b中選出a,b後,為使總和為0,所以應使c+d = -a-b。因此列舉c、d中取數字,然後計算所有情況的和,將其排個序,這樣就可以二分搜尋了,時間複雜度是o(n2logn)。
**模板:
#include
#include
using
namespace std;
const
int n =
4010
;long
long n,a[n]
,b[n]
,c[n]
,d[n]
,sum[n*n]
,ans;
//sum是c和d數列所有和的陣列
void
solve()
} cout << ans << endl;
}int
main()
lower_bound和upper_bound函式具體可以參考此文章
我們現在有體積和價值分別為v和w的n個物品。現在從這些物品中選出體積不超過m的物品放進揹包中,求所有挑選方案中價值總和的最大值。
(1 <= n <= 40,1 <= wi,vi
<= 1015,1 <= m <= 1015)
思路:這就是乙個很基礎的01揹包問題,但我們知道,01揹包的時間複雜度是o(nm),n是物品數,m是揹包體積,因為我們用了兩層迴圈一層迴圈物品,一層迴圈體積。但此處的揹包體積非常大,達到了1015,若再用nm的複雜度肯定會超時,所以針對這個體積超大的揹包,我們應該利用n比較小這一特點,去考慮問題。
最簡單的想法是列舉所有選物品的情況,但挑選物品的方法共有2n種,所以不能直接列舉,但我們可以拆成兩部分,分別列舉,220的複雜度還是可以接受的。
當我們把前半部分所有情況列舉出來後,如何判斷後面一部分呢?記前半部分一種選取方法對應的體積和價值總和分別為v1和w1。這樣在後半部分尋找總體積v2 <= m-v1時使v2達到最大就好了。我們要思考從所有列舉的(v2,w2)的集合中高效的找到max,因此我們可以排除所有的v2[i] < v2[j]且w1[i] > w1[j]的 j的情況,這一點可以按照v2,w1排序篩出來。得到的所有元素都滿足v2[i] < v2[j]且w2[i] < w2[j](如下圖),要計算max只需要找到滿足v2 <= m-v1的最大i就可得到,這裡可以用二分完成。所以總的時間複雜度是o(2(n/2)n)。
如下圖,若某種選法的總體積大於前乙個但總價值還小於前乙個(紅圈內的情況),那我們肯定不選擇這種選法,所以我們直接刪去這種選法來簡化計算。
**詳解:
#include
#include
#include
using
namespace std;
typedef
long
long ll;
const
int n =50;
const ll inf =
0x3f3f3f3f
;ll n,m,v[n]
,w[n]
;vector
> p;
//p存第一次列舉出的所有的v和w
void
solve()
} p.
push_back()
;//存入p陣列
}sort
(p.begin()
,p.end()
);//對p排序
//篩除必不可能選的元素
int num =1;
//num來存下所有可以選的物品
for(
int i =
1;i <
1<< nn;i++
)//迴圈所有物品(此時已經按體積從小到大排好序了)
if(p[num-1]
.second < p[i]
.second)
//如果當前的價值大於剛才存的最後乙個的價值,就把這個存進去
p[num++
]= p[i]
;//存進去
//列舉後半部分
ll ans =0;
for(
int i =
0;i <
1<<
(n-nn)
;i++
)//迴圈後半部分的所有情況}if
(v2 <= m)
//如果只選取後半部分的體積已經超過了m就不用再判斷了
} cout << ans << endl;
//輸出答案即可
}int
main()
MySQL SQL之常用小技巧
一 返回插入新記錄的id insert into 表名 字段列表 output inserted.id values 字段值列表 二 條件式排序 select from 表名 order by case when 欄位名 某值 then 1 else 0 end asc desc 三 清空表 tru...
C 的列舉小技巧
列舉是什麼呢?列舉是乙個值型別,包含一組命名的常量,如以下的 public enum color 這裡是最簡單的定義列舉的方式,沒有設定任何的東西,所以預設情況下,enum的型別是int型的,並且是從0開始往下遞增地,但是我們可以修改以上說的東西,比如以下 public enum color sho...
列舉平方數 小技巧
給出長度為n的序列a,求有多少對數對 i,j 1 i j n 滿足 ai aj 為完全平方數。第一行乙個整數 n 1 n 105 第二行 n 個整數 ai 1 ai 105 輸出乙個整數,表示滿足上述條件的數對個數。示例1 複製3 1 3 6 複製2 滿足條件的有 1,2 2,3 兩對。有幾個需要注...