P2157 [SDOI2009]学校食堂
P2157 [SDOI2009]學(xué)校食堂
題意:
小F 的學(xué)校在城市的一個(gè)偏僻角落,所有學(xué)生都只好在學(xué)校吃飯。學(xué)校有一個(gè)食堂,雖然簡(jiǎn)陋,但食堂大廚總能做出讓同學(xué)們滿意的菜肴。當(dāng)然,不同的人口味也不一定相同,但每個(gè)人的口味都可以用一個(gè)非負(fù)整數(shù)表示。 由于人手不夠,食堂每次只能為一個(gè)人做菜。做每道菜所需的時(shí)間是和前一道菜有關(guān)的,若前一道菜的對(duì)應(yīng)的口味是a,這一道為b,則做這道菜所需的時(shí)間為(a or b)-(a and b),而做第一道菜是不需要計(jì)算時(shí)間的。其中,or 和and 表示整數(shù)逐位或運(yùn)算及逐位與運(yùn)算,C語言中對(duì)應(yīng)的運(yùn)算符為“|”和“&”。
學(xué)生數(shù)目相對(duì)于這個(gè)學(xué)校還是比較多的,吃飯做菜往往就會(huì)花去不少時(shí)間。因此,學(xué)校食堂偶爾會(huì)不按照大家的排隊(duì)順序做菜,以縮短總的進(jìn)餐時(shí)間。
雖然同學(xué)們能夠理解學(xué)校食堂的這種做法,不過每個(gè)同學(xué)還是有一定容忍度的。也就是說,隊(duì)伍中的第i 個(gè)同學(xué),最多允許緊跟他身后的Bi 個(gè)人先拿到飯菜。一旦在此之后的任意同學(xué)比當(dāng)前同學(xué)先拿到飯,當(dāng)前同學(xué)將會(huì)十分憤怒。因此,食堂做菜還得照顧到同學(xué)們的情緒。 現(xiàn)在,小F 想知道在滿足所有人的容忍度這一前提下,自己的學(xué)校食堂做完這些菜最少需要多少時(shí)間。
題解:
狀壓dp不難看出,但是不會(huì)寫
每個(gè)人既要考慮前面的人,也要顧慮后面的情況
如何確定動(dòng)態(tài)轉(zhuǎn)移方程:
綜上我們?cè)O(shè):f[i][st][k]:表示前i-1給人已經(jīng)吃完了,i以及i后面七個(gè)人的吃飯狀態(tài)是st,上一次吃飯的人的位置與i的偏移是k,的最短時(shí)間
例如:st=(00000011)(2進(jìn)制下),表示第i個(gè)和第i+1個(gè)吃了,其他沒吃
f初始化為正無窮,初始狀態(tài)為 :f[1][0][7]= 0;
為什么f[1][0][7]為初始狀態(tài),我認(rèn)為k=7表示第0位,而我們一上來就要更新第1個(gè)人,所以需要第0個(gè)人的狀態(tài)。
現(xiàn)在我們開始更新狀態(tài),
如果第i個(gè)人吃飯了,那我們可以考慮轉(zhuǎn)移到第i+1個(gè)人,即j&1==1(也就是j的第一位是1),此時(shí)i后面7個(gè)人的吃飯順序就不會(huì)再受到 i的影響了,因?yàn)樗麄儾豢赡懿尻?duì)到i的前面。此時(shí)轉(zhuǎn)移為:
f[i+1][j>>1][k-1]=min(f[i][j>>1][k-1],f[i][j][k])
j>>1是因?yàn)閖是相對(duì)于當(dāng)前選定對(duì)象的偏移量,對(duì)于i來說是j,那對(duì)于第i+1個(gè)人,j就要左移一位,k也同理
如果第i個(gè)人沒吃飯,沒辦法轉(zhuǎn)移到i+1,(因?yàn)閕+1之前的人還沒吃完飯)。此時(shí)我們需要從i以及i之后的7位中選出一個(gè)人來打飯,也就是枚舉l從0到7,此時(shí)轉(zhuǎn)移方程為:
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是計(jì)算做飯時(shí)間,i+k是上一個(gè)打飯的人,i+l是當(dāng)前要打飯的人,當(dāng)然如果i+l是第一個(gè)打飯的人,Time就是0,否則就是題目所說的計(jì)算值
注意:每個(gè)人都有自己的忍耐范圍,也就是并不是所有人都能接收自己后面7個(gè)人,所有我們要對(duì)于每個(gè)人,如果超出了忍耐范圍,我們就不考慮這個(gè)人。
代碼:
// Problem: P2157 [SDOI2009]學(xué)校食堂 // 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++) { //對(duì)于第i個(gè)人已經(jīng)吃飯for (int j= 0; j < (1 << 8); j++) { //從自己開始往后8個(gè)人的狀態(tài)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個(gè)還未吃飯,枚舉它后面的人int lim= INF_int;for (int l= 0; l < 8; l++) {if (!(j & (1 << l))) //如果第l個(gè)人吃飯了{if (i + l > lim)break;lim= min(lim, i + l + a[i + l].b);if (i + k) //不是第一個(gè)吃飯的{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 //第一個(gè)吃飯的{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(); }總結(jié)
以上是生活随笔為你收集整理的P2157 [SDOI2009]学校食堂的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 突然嘴巴有点歪是怎么回事
- 下一篇: 起床发现漱口嘴巴包不住水了这怎么回事?