1. 程式人生 > 實用技巧 >P2157 [SDOI2009]學校食堂

P2157 [SDOI2009]學校食堂

我的這個方法比較奇怪,我是用佇列來實現轉移的。

首先分析一下題目性質。如果\(x\)是打完飯的人當中排隊最靠後的,那麼1到x-8這些人肯定都打完飯了。定義狀態\(f_{i,j,k}\)表示標號最大的打到飯的是\(i\)(有點拗口),最後一個打飯的是\(i-k\)\(k\)的範圍是0到7,\(i-1\)\(i-7\)的狀態是\(j\),需要的最少的時間。

這種狀態定義沒有後效性(想想為什麼),只是不好確定轉移的順序。核心操作來了,其實說白了就是bfs,我們可以用佇列存狀態,每次取隊首向外一層一層的擴充套件。這種方法還附帶剪枝的功能,跑的飛快。

程式碼實現比較繁瑣,注意要把不合法的狀態及時排除掉。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define forg(i,x) for(int i=first[x];i;i=nxt[i])
#define uu unsigned
#define fi first
#define se second
#define od(x) ((x)&1)
#define ev(x) (od(x)^1)
#define mi2(x) (1<<(x))
#define gw(x,j) ((x)>>(j)&1)
//取出二進位制數的某一位
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

const int inf=0x3f3f3f3f;
int n,t[1003],b[1003],dp[1003][8][1<<7],lim;
int nn;
struct dul{
	int a,b,c;
}q[2000003];
inline bool vad1(int i,int zi,int k){
	//if(b[i-k]<k)return 0;
	for(int ii=k+1;ii<=7;++ii)if(!gw(zi,ii-1)&&b[i-ii]<ii-k)return 0;
	return 1;
}
inline bool vad2(int i,int zi,int k){
	if(i+k>n)return 0;
	for(int ii=1;ii<=7;++ii)if(!gw(zi,ii-1)&&b[i-ii]<k+ii)return 0;
	for(int ii=1;ii<k;++ii)if(b[i+ii]<k-ii)return 0;
	return 1;
}
inline void pr2(int zi){for(int k=1;k<=7;++k)printf("%d",gw(zi,k-1));}

int main(){
	lim=(1<<7)-1;
    int cases;scanf("%d",&cases);while(cases--){
    	scanf("%d",&n);for(int i=1;i<=n;++i)scanf("%d%d",t+i,b+i);
    	memset(dp,0x3f,sizeof(dp));
    	int qh=1,qt=0;
    	q[++qt]=(dul){0,0,lim},dp[0][0][lim]=0;
    	
    	
    	while(qh<=qt){
    		int i=q[qh].a,j=q[qh].b,zi=q[qh].c;++qh;
    		for(int k=1;k<=7;++k)if(!gw(zi,k-1)&&vad1(i,zi,k)){
    			int &f=dp[i][k][zi|mi2(k-1)];
    			if(f==inf)q[++qt]=(dul){i,k,zi|mi2(k-1)};
    			f=min(f,i==0?0:dp[i][j][zi]+(t[i-j]^t[i-k]));
    		}
    		for(int k=1;k<=7;++k)if(vad2(i,zi,k)){
    			int zz=(zi<<k)|mi2(k-1);zz&=lim;
    			int &f=dp[i+k][0][zz];
    			if(f==inf)q[++qt]=(dul){i+k,0,zz};
    			f=min(f,i==0?0:dp[i][j][zi]+(t[i-j]^t[i+k]));
    		}
    	}
    	int ans=inf;
    	for(int k=0;k<=7;++k)ans=min(ans,dp[n][k][lim]);
    	printf("%d\n",ans);
    }
    return 0;
}