POJ2373 Dividing the Path
阿新 • • 發佈:2020-07-31
題意描述
在一條長為 \(L\) 的鏈上,有 \(n\) 個不同的特殊區間,要用多個長度為 \(2\times i(a\leq i\leq b)\) 的區間來覆蓋它們。
要求區間之間不能有重複,而且 \(1\)~\(L\) 段都要覆蓋到,同時每個特殊區間必須用同一個區間來覆蓋它。
求出區間的最少數量,如果無法達到輸出 \(-1\)。
演算法分析
這...,就是 DP 吧。
設 \(f(i)\) 表示前 \(1\)~\(i\) 段全覆蓋的最小區間使用量。
顯然 \(i\) 為 \(2\) 的倍數:
\(f(i)=min_{i-2\times b\leq j\leq i-2\times a}\{f(j)+1\}\)
但是還要求 \(i\) 不在某個特殊區間內部,因為特殊區間是不能被分割的。
那麼如何判斷某個點是否在一個特殊區間內部呢?
其實處理方式很簡單:
每讀入一個特殊區間就將那段區間的每個數都加 \(1\),如果某個點的數為 \(0\),就表示它不在某個特殊區間內部。
具體實現用差分:
int vis[N];
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
int x=read(),y=read();
vis[x+1]++;vis[y]--;
}
for(int i=1;i<=l;i++) vis[i]+=vis[i-1];
然後開開心心 TLE,原因是找 \(min\{f(j)\}\)
於是就需要一種資料結構記錄每一段 \(2\times a\)~\(2\times b\) 的最小值——單調佇列。
結束。
程式碼實現
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<queue> #define N 1000010 #define INF 0x3f3f3f3f using namespace std; int n,l,a,b,f[N],vis[N]; deque<int>q; int read(){ int x=0,f=1;char c=getchar(); while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' && c<='9') x=x*10+c-48,c=getchar(); return x*f; } int main(){ //freopen("divide.in","r",stdin); //freopen("divide.out","w",stdout); n=read(),l=read(),a=read(),b=read(); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++){ int x=read(),y=read(); vis[x+1]++;vis[y]--; } for(int i=1;i<=l;i++) vis[i]+=vis[i-1]; memset(f,0x3f,sizeof(f)); f[0]=0; for(int i=2*a;i<=l;i+=2){ while(!q.empty() && f[q.back()]>=f[i-2*a]) q.pop_back(); q.push_back(i-2*a); while(!q.empty() && q.front()<i-2*b) q.pop_front(); if(vis[i]) continue; if(!q.empty()) f[i]=f[q.front()]+1; } if(f[l]>=INF) puts("-1"); else printf("%d\n",f[l]); //fclose(stdin);fclose(stdout); return 0; }
完結撒花