状压dp
是一种很重要的
,其基本思路是利用二进制,简化我们状态的定义。
为了更好的理解状压,我们需要先来看看C++
中的二进制运算:
&
:二进制按位与运算符,格式为a&b
,如果 和 的二进制的同一位置上的数码都是 ,那么答案的该位数码也是 ,否则就是 。例如, 。|
:二进制按位或运算符,格式为a|b
,如果 和 的二进制的同一位上的数码都是 ,那么答案的该位数码也是 ,否则就是 。例如: 。<<
:这不是输出的符号吗?其实它还有另一个意思:左移运算符。格式:a<<b
。就是把一个数的二进制的最高的 位删了,在最低位加上 个 ,例如 。由此可见,一般而言, ,只不过运算使得更快。>>
:它与左移运算符类似,叫右移运算符,操作和左移差不多,只不过是把最低的 位删了,在最高位加上 个 而已,一般而言, 。^
:二进制按位异或运算符,格式为a^b
,如果 和 二进制的同一位不同时为 或 ,那么答案的该位就是 ,否则为 ,例子就不举了,值得一提的是, ^ 。
今天,我们就讲到这里,明天我们再讲。
记 表示第 个人已经打完饭,第 到第 个人是否打完饭的状态为 ,且上一个打饭的人是 的最小花费。转移看代码。
int f[1010][16][300];
int T[1010],B[1010],n;
int test_number,inf;
inline int cost(int a,int b){
return (T[a]|T[b])-(T[a]&T[b]);
}
void ckmin(int &a,int b){a=min(a,b);}
int main(){
freopen("t1.in","r",stdin);
scanf("%d",&test_number);
while (test_number--){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&T[i],&B[i]);
memset(f,127,sizeof(f));
inf=f[1][1][1];f[1][7][0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<256;j++)
for(int k=-8;k<=7;k++)
if (f[i][k+8][j]!=inf){
if (j&1) ckmin(f[i+1][k+7][j>>1],f[i][k+8][j]);
// 如果第i个人已经打完饭了,直接转移至下一个人
if ((j&1)==0){//第i个人没吃
register int lir=inf;
for(int h=0;h<=7;h++)
if (!((j>>h)&1)){
if (i+h>lir) break;
ckmin(lir,i+h+B[i+h]);
ckmin(f[i][h+8][j|(1<<h)],f[i][k+8][j]+(i+k?cost(i+k,i+h):0));
}
}
}
register int res=inf;
for(int k=-8;k<=0;k++)
res=min(res,f[n+1][k+8][0]);
printf("%d\n",res);
}
return 0;
}