本题状压DP。

一个人打饭的状态最多由后面7个人影响,于是考虑状压DP。

定义状态f[i][j][k]表示前i-1个人打完饭,第i个人和其后面7个人打饭状态为j,最后一个打饭的人编号为k的最小打饭时间(k含义相对于i,即编号为i+k,k∈[-8,7])。

状态转移:

1、当第i个人已经打完饭(即j&1为真),则后面的人不能再影响到第i个人当前的状态,所以由f[i][j][k]直接转移到f[i+1][j>>1][k-1]。

2、否则,就枚举第i个人和其往后的7个人中选出一个还没打饭的人来打饭,因为还得满足b的限制条件,所以用tp表示当前的最小限制范围(因为可能出现诸如第i人允许第i+7人插队,但第i+1人最多允许第i+1+4人插队的情况,转移必须做到对所有b合法),然后转移就非常简单了。

最后末状态就是f[n+1][0][j]的最小值。

#include <cstdio>
#include <cstring>
#define maxn_N 255
#define INF 0x3f3f3f3f
using namespace std;
int t,n,dp[1005][maxn_N+1][16],ti[1005],bi[1005],ans;
/*
t表示轮数
dp[i][j][k]
表示
前i-1个人已经打完饭了,
第i个人和后面的7个人的吃饭状态是j,
最后一个打完饭的人相对于i的编号(距离)是k的最少打饭时间 ps: k范围是-8到7
因为最后状态是转移到n+1,也就是n 前面最多可以插7个人,转移到n+1则,再-1
j位置初始化大小一定要是256!!!!!!!!!qvq

ti[i]表示第i个人的口味  bi[i]表示当前第i同学只允许后面bi[i]的人插队
*/
int min(int a,int b){return a>b?b:a;}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&ti[i],&bi[i]);
        memset(dp,0x3f,sizeof(dp));
        dp[1][0][7]=0;//这样初始化表示前0个人打完饭了,当前第i个人和后面7个人的吃饭状态为0,最后一个吃饭是7 也就是(7-8)=-1 也就是最后一个吃饭的是第0个人
        for(int i=1;i<=n;i++)
            for(int j=0;j<=maxn_N;j++)
                for(int k=-8;k<=7;k++)
                    if(dp[i][j][k+8]!=INF){//为了找到有最后一个打完饭的人(相对于i的编号) 的合法状态转移
                        if(j&1){
                            dp[i+1][j>>1][k+7]=min(dp[i][j][k+8],dp[i+1][j>>1][k+7]);
                            /*如果当前j状态下,第i个人已经打完饭了,
                            则不会因为后面的人插不插队而生气,
                            所以状态直接转移到下一个人上,
                            同时j也要右移表示第i个人已经打完饭,
                            k也要+7,因为是相对于i的最后一个打完饭的人
                            */
                        }else{
                            int sm=INF;//这个sm为当前j状态下,维护前面的同学的容忍插队程度
                            for(int fk=0;fk<=7;fk++){
                                if(!((j>>fk)&1)){//首先要确保,第i+fk位置,的人,有没有打完饭
                                if(i+fk>sm)break;//如果后面的人先吃饭,前面有些人的比较小气,就不行
				//如果后面选到插队的这个人的 编号,大于sm的范围,也就是前面的人 的容忍度,则不可以插队
                                sm=min(sm,i+fk+bi[i+fk]);
                                dp[i][j|(1<<fk)][fk+8]=min(dp[i][j|(1<<fk)][fk+8],dp[i][j][k+8]+(i+k?(ti[i+k]^ti[i+fk]):0));
                                /*
                                上面这一句表示,前i-1个人已经打完饭了,
                                当前打饭状态为j|(1<<fk) 也就是让第i+fk个人先打饭,
                                最后一个打完饭的编号是 fk+8,
                                也就是第i+fk位同学最后打完饭
                                
                                也就是从dp[i][j][k+8]的状态转移过来的,因为,dp[i][j][k+8]状态下,第i后面的同学还没有插队
                                i+k表示最后一个打完饭的编号,如果小于等于0也就是不存在最后一个打完饭的则直接返回0
                                */
                                }
                            }
                        }
                    }
                    ans=INF;
                    //为何要从0-8的状态下找,因为 0-8 是当前第i位 的前面的同学,9-15 是后面的同学, 最后的状态会转移到n+1也就是后面不会有同学,所以 最后一个打完饭的同学相对于i的编号一定是在0-8里面
                    for(int i=0;i<=8;i++)ans=min(ans,dp[n+1][0][i]);
                    printf("%d\n",ans);
    }
    return 0;
}