P2157 [SDOI2009] 学校食堂
P2157 [SDOI2009] 学校食堂
题目描述
小 F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭。学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴。当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示。 由于人手不够,食堂每次只能为一个人做菜。做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是 |
和 &
。
学生数目相对于这个学校还是比较多的,吃饭做菜往往就会花去不少时间。因此,学校食堂偶尔会不按照大家的排队顺序做菜,以缩短总的进餐时间。
虽然同学们能够理解学校食堂的这种做法,不过每个同学还是有一定容忍度的。也就是说,队伍中的第
输入格式
第一行包含一个正整数
输出格式
包含
数据规模和约定
- 对于
的数据,满足 。
Solution:
好久之前写的状压 dp 了,首先我们主要到
记数组
对于状态转移:
如果在当前状态
那么如果当前节点
然后次打饭产生的花费就是
所以我们可以列出形式化的状态转移方程:
然后时间复杂度是
Code:
#include<bits/stdc++.h> const int N=1005; const int inf=1e9; const int k_0=8; using namespace std; int dp[N][1<<8][20],t[N],b[N]; //dp[i][idx][k] : [1,i-1] 已经全打完饭了 [i,i+7] 的打饭状态是 idx 上一个打饭的是 i+k int n,c; void work() { cin>>n; for(int i=1;i<=n;i++) { scanf("%d%d",&t[i],&b[i]); } memset(dp,126,sizeof(dp)); dp[1][0][-1+k_0]=0;//防止爆负数 int idx=(1<<8)-1; int last,now; for(int i=1;i<=n;i++) { for(int j=0;j<=idx;j++) { for(int k=-8;k<=7;k++) { last=i+k; if(dp[i][j][k+k_0]==dp[0][0][0])continue;//当前状态的花费为 inf 状态不合法 if(j&1) { dp[i+1][j>>1][k-1+k_0]=min(dp[i+1][j>>1][k-1+k_0],dp[i][j][k+k_0]); // j>>1: 把当前位顶掉,k-1 :last = i+k =(i+1)+(k-1) // 如果当前位已经打饭了,转移到 i+1 } else { int lim=n;//否则枚举一下下一个打饭的是谁,转移 for(int l=0;l<=7;l++) { now=i+l; if((1<<l)&j)continue; if(now>lim)break; lim=min(lim,now+b[now]);//要满足所有 [i,now] 的 b 对 now 的限制 int cost=t[last]^t[now]; if(!last)cost=0;//第一个打饭不产生花费 dp[i][j|(1<<l)][l+k_0]=min(dp[i][j|(1<<l)][l+k_0],dp[i][j][k+k_0]+cost); } } } } } int ans=inf; for(int k=-8;k;k++) { ans=min(ans,dp[n+1][0][k+k_0]); } printf("%d\n",ans); } int main() { freopen("dining.in","r",stdin); freopen("dining.out","w",stdout); int T; cin>>T; while(T--) { work(); } return 0; }