题解 P2780 【[AHOI2016初中组]游戏】
官方题解
为了找出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%的数据。
std
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <cmath>
using namespace std ;
const int R = 60 ;
const int MAXN = 609 ;
const int MAXB = 180009 ;
const int __MAXK = 13 ;
int n , k , a , b , x[MAXN] , k0 , sumx[MAXN] ;
long long F[MAXB][__MAXK] ;
void Init() {
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
cin >> n >> k >> a >> b ;
for ( int i = 1 ; i <= n ; i ++ ) cin >> x[i] ;
sumx[0] = 0 ;
for ( int i = 1 ; i <= n ; i ++ ) sumx[i] = sumx[i-1] + x[i] ;
}
void Solve() {
k0 = (k+1) / R ;
if ( R * k0 < k+1 ) k0 ++ ;
for ( int j = 0 ; j <= b ; j ++ )
for ( int k = 0 ; k < k0 ; k ++ )
F[j][k] = 0 ;
F[0][0] = 1 ;
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = sumx[i] ; j >= 0 ; j -- )
for ( int k = 0 ; k < k0 ; k ++ ) {
if ( k + 1 < k0 ) F[j+x[i]][k+1] |= (F[j][k] >> (R-1)) ;
F[j+x[i]][k] |= (F[j][k] << 1) ;
}
vector<int> ans ; ans.clear() ;
for ( int j = 0 ; j <= b ; j ++ )
if ( ((F[j][k0-1] >> (k+1 - R*(k0-1) - 1)) & 1) == 1 )
ans.push_back(j) ;
int ret = 0 ;
int near2a = abs(ans[0] - a) , near2b = abs(ans[0] - b) ;
for ( int i = 1 ; i < ans.size() ; i ++ ) {
if ( abs(ans[i] - a) < near2a ) near2a = abs(ans[i] - a) ;
if ( abs(ans[i] - b) < near2b ) near2b = abs(ans[i] - b) ;
int rr = (ans[i] - ans[i-1]) / 2 ;
if ( ( a <= ans[i-1] + rr && ans[i-1] + rr <= b ) || ( a <= ans[i] - rr && ans[i] - rr <= b ) ) {
if ( rr > ret ) ret = rr ;
}
}
if ( near2a > ret ) ret = near2a ;
if ( near2b > ret ) ret = near2b ;
cout << ret << "\n" ;
fprintf(stderr,"%d\n" , ret) ;
}
int main() {
Init() ;
Solve() ;
}
C++福利
如果有个75000位的二进制数类型多好啊。
诶,STL好像有个bitset。
嗯,好像可以直接用……
稍微跑得比std慢的AC程序
#include <iostream>
#include <cstdio>
#include <bitset>
using namespace std;
const size_t MaxN(251), MaxSum(75001);
const int INF(0x7f7f7f7f);
int N, K, A, B;
int Xi, Sum;
bitset<MaxSum> F[MaxN];
int L[MaxSum], R[MaxSum];
int Ans(-INF);
int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
cin >> N >> K >> A >> B;
F[0].set(0);
for(int i(1); i <= N; i++) {
scanf("%d", &Xi);
for(int j(K); j; j--)
F[j] |= F[j - 1] << Xi;
Sum += Xi;
}
L[A] = -INF;
for(int i(A); ~i; i--)
if(F[K][i]) {
L[A] = i;
break;
}
for(int i(A + 1); i <= B; i++)
L[i] = F[K][i] ? i : L[i - 1];
R[B] = INF;
for(int i(B); i <= Sum; i++)
if(F[K][i]) {
R[B] = i;
break;
}
for(int i(B - 1); i >= A; i--)
R[i] = F[K][i] ? i : R[i + 1];
for(int i(A); i <= B; i++)
Ans = max(Ans, min(i - L[i], R[i] - i));
cout << Ans << endl;
return 0;
}