傳送門~
題目大意
先分析
給定
題目分析
但是具體怎麼定義狀態呢?
假如說(最一般的想法)
但是推的時候要涉及到上一個人的打飯狀態…
而上一個打飯的人不一定是
所以我們還要記錄一下上一個打飯的人…
定義狀態
很顯然
然後就是考慮遞推了.
初始化的話 因爲要找最小值 全都賦值爲
∞ …
邊界條件f1,0,−1=0 顯然.首先如果
j&1≠0 , 說明第i 個人已經打飯了, 後面的人就不會跑到他前面…
我們發現這個狀態和fi+1,j>>1,k−1 (第i+1 個人打飯, 集合爲去掉i 後的狀態, 最後一個打飯的人是(i+1)+(k−1) 是一樣的.. 可以直接轉移過去.- 如果
j&1=0 呢? 說明第i 個人還沒有打飯. 那就不能轉移到fi+1,?,? 了.
我們就要從後面枚舉一個人, 讓他去打飯.
我們可以1∼7 枚舉l , 目標狀態就是fi,j|(1<<l),l …
於是就出現了fi,j|(1<<l),l=min{fi,j,k+ti+k xor ti+l} …
但是要注意第一道菜是不需要時間的, 所以要特判i+k=0 的情況…
然後要注意的就是枚舉的這個人不能引起別人的憤怒…
所以要維護一下能忍耐的範圍…
一旦不能忍耐了, 那就直接break掉就行.. 因爲後面的更不行了… - 最後從
fn+1,0,? 裏面找個最小的作爲ans 就好了~
這樣就做完了.
代碼:
這種枚舉變量個數多的dp用的tab縮進真是美如畫..
#include <cstdio>
#include <cstring>
const int N=1002;
int f[N][260][17],t[N],b[N];
inline int gn(int a=0,char c=0){
for(;c<'0'||c>'9';c=getchar());
for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
inline int min(const int &a,const int &b){return a<b?a:b;}
void work(){
memset(f,0x3f,sizeof(f)); int n=gn();
for(int i=1;i<=n;++i) t[i]=gn(),b[i]=gn();
f[1][0][7]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<256;++j)
for(int k=-8;k<8;++k)
if(f[i][j][k+8]<1e9){
if(j&1) f[i+1][j>>1][k+7]=min(f[i+1][j>>1][k+7],f[i][j][k+8]);
else{
int r=1e9;
for(int l=0;l<8;++l)
if(!(j&(1<<l))){
if(i+l>r) break;
if(i+l+b[i+l]<r) r=i+l+b[i+l];
f[i][j|(1<<l)][l+8]=min(f[i][j|(1<<l)][l+8],f[i][j][k+8]+(i+k?(t[i+k]^t[i+l]):0));
}
}
} int ans=1e9;
for(int i=-8;i<8;++i) ans=min(ans,f[n+1][0][i]); printf("%d\n",ans);
}
int main(){
int T=gn();
while(T--)work();
}
注意事項
注意事項應該都說過了…
可能要提醒的就是多組數據, 每次記得清理f數組…
然後就是該有的特判都不要少..
一定時刻記得第三維要+8哦~
完結撒花~