1. 程式人生 > >[JZOJ3521]道路覆蓋--狀壓DP

[JZOJ3521]道路覆蓋--狀壓DP

題目連結

略略略

分析

首先一看到使得最低的高度最高就想到了二分,於是就轉化成了一個是否可行的問題

發現這個\(k\)都很小,考慮使用狀態壓縮DP

但是我一開始發現似乎並不好設計狀態...如果這個\(k\)表示前\(k\)個方塊的狀態有沒有開始塗似乎不好轉移

看了solution發現我還是\(Too Young Too Simple\)

我們用對於第\(i\)塊,對它決策有影響的只有它前面的\(i-k+1\)塊的狀態,於是我們只需要用一個\(k\)位二進位制串表示狀態來從\(i\)遞推到\(i+1\).對於塊\(p\)二進位制串的第\(i\)位(0位開始)表示第\(p-(k-i-1)\)

塊的狀態

\(f[i][s]\)表示已經從前往後決策完第\(i\)塊,\(i-k+1\)\(i\)的狀態為\(s\)的最小代價,這些狀態保證都是合法的(即所有的高度等於等於二分值)

這時候從\(i\)遞推到\(i+1\)的話我們需要知道之前\(i+1\)之前\(k\)塊能累高\(i+1\)塊多少高度,這直接掃一遍就好了

如果之前累加的高度\(dta\)+\(h[i+1]\)大於等於二分的高度,那麼我們可以不選塗第\(i+1\)

\(f[i+1][s>>1]=min(f[i+1][s>>1],f[i][s])\),這時候第\(k-1\)位為0表示\(i+1\)位沒塗

如果\(dta+h[i+1]+e[i+1]\)大於等於二分值,那麼我們就可以塗

\(f[i+1][s>>1|(1<<(k-1))]=min(f[i+1][s>>1|(1<<(k-1))],f[i][s]+c[i+1])\)

意義和第一個類似表示\(i+1\)位塗的狀態

最後判斷是否有狀態\(f[n][s]<=m\)就好了

感覺這題還是挺不錯的,通過考慮那些狀態會影響決策來減小決策空間,表示狀態的方法也比較獨特(可能是我太菜做的題少)

同時從大佬程式碼中學到了一個優化就是可行性剪枝,對於\(f[i][s]\)已經大於等於\(m\)

的我們直接不管

程式碼

/*
  code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
    while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=1005;
const int inf=0x7fffffff-1926817;
int f[maxn][1<<11];
int S,n,m,k;
int h[maxn],c[maxn],e[maxn];
inline  bool chk(int qwq){
    for(ri i=0;i<=n;i++)for(ri j=0;j<S;j++)f[i][j]=inf;
    f[0][0]=0;
    int num=0;
    for(ri o=0;o<n;o++){
        for(ri i=0;i<S;i++){
            if(f[o][i]>m)continue;//優化 
            num=0;
            for(ri j=1;j<k;j++){
                if(o-(k-j)+1<=0)continue;
                if(i&(1<<j))num+=e[o-(k-j)+1];
            }
            if(num+h[o+1]>=qwq){
                f[o+1][i>>1]=min(f[o+1][i>>1],f[o][i]);
            }
            if(num+h[o+1]+e[o+1]>=qwq){
                f[o+1][i>>1|(1<<(k-1))]=min(f[o+1][i>>1|(1<<(k-1))],f[o][i]+c[o+1]);
            }
        }
    }
    for(ri j=0;j<S;j++)if(f[n][j]<=m)return 1;
    return 0;
}
int main(){
    int ans=0;
    FO(cover)
    read(n),read(m),read(k);
    for(ri i=1;i<=n;i++){
        read(h[i]),read(e[i]),read(c[i]);
    }
    S=1<<k;
    int mid,L=0,R=1e6+5;
    while(L<=R){
        mid=(L+R)>>1;
        if(chk(mid))ans=mid,L=mid+1;
        else R=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}