P2157 [SDOI2009]学校食堂
P2157 [SDOI2009]學校食堂
題意:
小F 的學校在城市的一個偏僻角落,所有學生都只好在學校吃飯。學校有一個食堂,雖然簡陋,但食堂大廚總能做出讓同學們滿意的菜肴。當然,不同的人口味也不一定相同,但每個人的口味都可以用一個非負整數表示。 由于人手不夠,食堂每次只能為一個人做菜。做每道菜所需的時間是和前一道菜有關的,若前一道菜的對應的口味是a,這一道為b,則做這道菜所需的時間為(a or b)-(a and b),而做第一道菜是不需要計算時間的。其中,or 和and 表示整數逐位或運算及逐位與運算,C語言中對應的運算符為“|”和“&”。
學生數目相對于這個學校還是比較多的,吃飯做菜往往就會花去不少時間。因此,學校食堂偶爾會不按照大家的排隊順序做菜,以縮短總的進餐時間。
雖然同學們能夠理解學校食堂的這種做法,不過每個同學還是有一定容忍度的。也就是說,隊伍中的第i 個同學,最多允許緊跟他身后的Bi 個人先拿到飯菜。一旦在此之后的任意同學比當前同學先拿到飯,當前同學將會十分憤怒。因此,食堂做菜還得照顧到同學們的情緒。 現在,小F 想知道在滿足所有人的容忍度這一前提下,自己的學校食堂做完這些菜最少需要多少時間。
題解:
狀壓dp不難看出,但是不會寫
每個人既要考慮前面的人,也要顧慮后面的情況
如何確定動態轉移方程:
綜上我們設:f[i][st][k]:表示前i-1給人已經吃完了,i以及i后面七個人的吃飯狀態是st,上一次吃飯的人的位置與i的偏移是k,的最短時間
例如:st=(00000011)(2進制下),表示第i個和第i+1個吃了,其他沒吃
f初始化為正無窮,初始狀態為 :f[1][0][7]= 0;
為什么f[1][0][7]為初始狀態,我認為k=7表示第0位,而我們一上來就要更新第1個人,所以需要第0個人的狀態。
現在我們開始更新狀態,
如果第i個人吃飯了,那我們可以考慮轉移到第i+1個人,即j&1==1(也就是j的第一位是1),此時i后面7個人的吃飯順序就不會再受到 i的影響了,因為他們不可能插隊到i的前面。此時轉移為:
f[i+1][j>>1][k-1]=min(f[i][j>>1][k-1],f[i][j][k])
j>>1是因為j是相對于當前選定對象的偏移量,對于i來說是j,那對于第i+1個人,j就要左移一位,k也同理
如果第i個人沒吃飯,沒辦法轉移到i+1,(因為i+1之前的人還沒吃完飯)。此時我們需要從i以及i之后的7位中選出一個人來打飯,也就是枚舉l從0到7,此時轉移方程為:
f[i][j | (1 << l)][l + 8]= min(f[i][j | (1 << l)][l + 8], f[i][j][k + 8] + Time(a[i + k].t, a[i + l].t));
Time是計算做飯時間,i+k是上一個打飯的人,i+l是當前要打飯的人,當然如果i+l是第一個打飯的人,Time就是0,否則就是題目所說的計算值
注意:每個人都有自己的忍耐范圍,也就是并不是所有人都能接收自己后面7個人,所有我們要對于每個人,如果超出了忍耐范圍,我們就不考慮這個人。
代碼:
// Problem: P2157 [SDOI2009]學校食堂 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P2157 // Memory Limit: 125 MB // Time Limit: 800 ms // Data:2021-08-12 13:00:56 // By Jozky#include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; template <typename T> inline void read(T& x) {T f= 1;x= 0;char ch= getchar();while (0 == isdigit(ch)) {if (ch == '-')f= -1;ch= getchar();}while (0 != isdigit(ch))x= (x << 1) + (x << 3) + ch - '0', ch= getchar();x*= f; } template <typename T> inline void write(T x) {if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0'); } void rd_test() { #ifdef ONLINE_JUDGE #elsestartTime= clock();freopen("in.txt", "r", stdin); #endif } void Time_test() { #ifdef ONLINE_JUDGE #elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } int f[1004][1 << 9][20]; struct node {int t, b; } a[1030]; int base= 8; //偏移量 int Time(int x, int y) {return (x | y) - (x & y); } int main() {//rd_test();int t;read(t);while (t--) {int n;read(n);for (int i= 1; i <= n; i++) {read(a[i].t);read(a[i].b);}memset(f, INF_int, sizeof(f));f[1][0][7]= 0; //?/*f[i+1][j<<1][k-1]=min(f[i+1][j<<1][k-1],f[i][j][k]);//i+k是上一次打飯的人,i+h是枚舉本次打飯的人f[i][j|(1<<h)][h]=min(f[i][j|(1<<h)][h],f[i][j][k]+Time(i+k,i+h));*/for (int i= 1; i <= n; i++) { //對于第i個人已經吃飯for (int j= 0; j < (1 << 8); j++) { //從自己開始往后8個人的狀態for (int k= -8; k <= 7; k++) { //上一次吃飯的人距離i的位置if (f[i][j][k + 8] != INF_int) {if (j & 1) { //j的第一位是1,說明i本身吃飯了f[i + 1][j >> 1][(k + base) - 1]= min(f[i + 1][j >> 1][(k + base) - 1], f[i][j][k + base]);}else { //如果第i個還未吃飯,枚舉它后面的人int lim= INF_int;for (int l= 0; l < 8; l++) {if (!(j & (1 << l))) //如果第l個人吃飯了{if (i + l > lim)break;lim= min(lim, i + l + a[i + l].b);if (i + k) //不是第一個吃飯的{f[i][j | (1 << l)][l + base]= min(f[i][j | (1 << l)][l + base], f[i][j][k + base] + Time(a[i + k].t, a[i + l].t));}else //第一個吃飯的{f[i][j | (1 << l)][l + base]= min(f[i][j | (1 << l)][l + base], f[i][j][k + base] + 0);}}}}}}}}int res= INF_int;for (int k= 0; k <= 8; k++) {res= min(res, f[n + 1][0][k]);}printf("%d\n", res);}//Time_test(); }總結
以上是生活随笔為你收集整理的P2157 [SDOI2009]学校食堂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 突然嘴巴有点歪是怎么回事
- 下一篇: 起床发现漱口嘴巴包不住水了这怎么回事?