【work题解】
10.16
思路:
dp(i,j,k)考虑了前i件事,同时距离i最近的j件事的状态,k表示最近的做的事距离i的距离。
每次决策只有两种,先取出之间没做的某件事v做了,即
dp[i][j | (1 << v)][v] = min(dp[i][j | (1 << v)][v], dp[i][j][k] + ( (k == 19) ? a[i - v] : calc(a[i - v] , a[i - k] ) ) )
将第i件事压下暂时不做,即
dp[i + 1][(j^(1<<7))<<1][min(k + 1, 19)] =min(dp[i + 1][(j^(1<<7))<<1][min(k + 1, 19)], dp[i][j][k])
显然这两种转移已经包含了所有情况。
复杂度O(n∗28∗162)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 1050
using namespace std;
const int inf = 0x7f7f7f7f;
int n;
int a[N], b[N], dp[N][1 << 8][17] ;
inline int cal ( int x , int y ){ return (x | y) - (x & y); }
int main(){
freopen("work.in", "r", stdin);
freopen("work.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; ++i) scanf("%d%d", &a[i], &b[i]);
memset( dp , 0x7f , sizeof( dp ) );
dp[0][(1 << 8)-1][16] = 0;
for(int i=0; i<=n; ++i)
for(int j=0; j<(1 << 8); ++j)
for(int k=0; k<17; ++k)
if( dp[i][j][k] != inf ){
int ed = min(i , 8), limit = inf;
for(int v=ed-1; v>=0; --v)
if( (( j >> v) & 1) == 0 )//只能先做这些没做的事的b以内的事件
limit = min( limit, i - v + b[i-v] );
if( (j >> 7 & 1) == 1 ) //将第i件事压下暂时不做(保证先把下一步就做不了的事做了)
dp[i+1][(j^(1<<7))<<1][min(k+1, 16)] = min(dp[i+1][(j^(1<<7))<<1][min(k+1, 16)], dp[i][j][k]);
for(int v=ed-1; v>=0 && i-v<=limit; --v)
if( (j >> v & 1) == 0 )//先取出之间没做的某件事v
dp[i][j|(1<<v)][v] = min(dp[i][j|(1<<v)][v], dp[i][j][k]+((k==16)?a[i-v]:cal(a[i-v], a[i-k])));
}
int ans = inf;
for(int i=0; i<17; ++i) ans = min( ans, dp[n][(1<<8)-1][i] );
printf("%d\n" , ans );
return 0;
}