題1:
小雪與小可可正在玩一種數字遊戲。他們準備了n卡片,每一張卡片上都有乙個整數。遊戲開始後,小雪會先選擇乙個不小於a且不大於b的整數t,並告訴小可可這個數字t是多少。之後小可可會挑出恰好k張卡片,並將這k張卡片上的數字相加,得到的和數記為m。
小雪希望t和m差的絕對值盡可能大,而小可可卻希望t和m差的絕對值盡可能小。在遊戲開始前,他們二人都知道n,a,b和k是多少,也知道每一張卡片上的數字是多少。在小雪決定了t的大小後,不能再修改,之後才由小可可挑選紙牌。
小雪希望知道,在二人都嘗試最優策略的情況下,t和m差的絕對值最大可以有多大?
題解:
為了找出t和m的最大差,我們需要先找出所有可能的m,也就是要算出來有哪些數字可以通過在n個數字中挑選k個來得到。這乙個簡化版的01揹包問題,
記f[i][j][x]表示前i個數字中選出j個來
,是否可以組成數字x。這樣做時間複雜度是o(nkmaxb)的,可以通過85%的資料。
又可以發現f[i][j][x]全都是boolean型的,考慮把多個f[i][j][x]在最後一維進行壓縮,例如我們可以用乙個64位整數來表示64個boolean值。01揹包的所有轉移都可以用位運算來實現。這便可以通過100%的資料。
這樣的揹包dp真是沒見過,應該是我見識太少了qaq;
表示從前i個數中選出k個,是否可以組成的數x
,以前的f【i】【x】是從前i個數中隨便選,不管選幾個,問是否可以選出和為x的.
頭上這種稍微麻煩一點(不過好歹明白了)
不是滿分演算法,滿分演算法還是要加位運算,一般只有boolean的揹包動規,是可以用位運算的
#include#include#include#include#include#includeusing namespace std;
int f[69][69][19500],n,k,a,b,x[259];
int main()
{ scanf("%d%d%d%d",&n,&k,&a,&b);
for (int i=1;i<=n;i++) scanf("%d",&x[i]);
f[0][0][0]=1;//邊界,前0個數選0個組成0是可以的
for (int i=1;i<=n;i++)
{ for (int j=1;j<=min(k,i);j++)
{ for (int l=b;l>=x[i];l--)
f[i][j][l]=f[i-1][j-1][l-x[i]] || f[i-1][j][l];
//第i個數到底選不選,選:如果前i-1選j-1,可以組成l-x【i】,那麼f【i】【j】【l】=1
//不選x【i】:如果前i-1個數選j個,可以組成l,那就不用選x【i】
for (int l=0;l