勿忘把春夏之交vijos的比賽再看看

2021-07-11 13:03:23 字數 1370 閱讀 7828

題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