题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<algorithm> #include<string> #include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> using 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++) { <span style="white-space:pre"> </span>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];
<span style="white-space:pre"> </span>//第i个数到底选不选,选:如果前i-1选j-1,可以组成l-x【i】,那么f【i】【j】【l】=1
<span style="white-space:pre"> </span>//不选x【i】:如果前i-1个数选j个,可以组成l,那就不用选x【i】<span style="white-space:pre"> </span> for (int l=0;l<x[i];l++) f[i][j][l]=f[i-1][j][l];//前l<x【i】不能选x[i],那么就只有,以上的不选x【i】的那种情况了 } f[i][0][0]=1;//被这里坑了一段时间(没有考虑到,前i个数选0个,组成0是可以的) } /*for (int i=1;i<=5;i++) for (int j=1;j<=k;j++) for (int l=0;l<=18;l++) printf("%d %d %d %d\n",i,j,l,f[i][j][l]); */ int ans=0,last=0,now; for (int i=1;i<a;i++) if (f[n][k][i]) last=i; for (int i=a;i<=b;i++) if (f[n][k][i]) { if (last<a)//t在最小的时候,t==a时 { ans=min(a-last,i-a); last=i; } ans=max(ans,(i+last)/2-last),last=i;//t在两个数中间时,肯定方案最好 } //在这里想了半天 ans=max(ans,b-last);//t在最大的时候t==b时 printf("%d",ans); return 0; }