狀壓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;
}