Link:
Solution:
一开始想错方向的一道简单$dp$,不应该啊……
我一开始的想法是以$cows' ranges$的节点为状态来$dp$
但明显一个灌溉的区间的两边不一定都在$cows's ranges$上,
因此应该以长为$L$的$field$上的每一个偶数节点为状态来$dp$,
这样转移方程就很容易了:
$dp[i]=min\{ dp[j] \} +1(2*a\le i-j\le 2*b)$
由于$dp[j]$具有决策单调性(对于节点$i$,$k$比$j$更优$(j<k)$,那么对于后面任意一个节点$j$都优于$k$)
于是使用单调队列维护一个$dp$值递增,离起点距离递增的序列,每次弹出$i-q[l]>=2*b$的$l$
为了处理$cows' ranges$上的节点,使用差分将在区间内的节点打上标记,遇到时跳过即可
Note:这里的特判依然要注意,如果对于一个非终点的节点找不到符合的点,不代表无解!跳过即可
Code:
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> using namespace std; const int MAXN=1e6+10; int n,L,a,b,dp[MAXN],pre[MAXN],q[MAXN],l,r; int main() { scanf("%d%d%d%d",&n,&L,&a,&b); for(int i=1;i<=n;i++) { int x,y;scanf("%d%d",&x,&y); pre[x+1]++;pre[y]--; if(y-x>2*b){printf("-1");return 0;} } for(int i=1;i<=L;i++) pre[i]+=pre[i-1]; l=r=1;q[1]=0; for(int i=2*a;i<=L;i+=2) { if(pre[i]) continue; while(l<=r && i-q[l]>2*b) l++; if(l>r){printf("-1");return 0;} if(i-q[l]<2*a) continue; //对不符合条件的数的处理 dp[i]=dp[q[l]]+1; while(l<=r && dp[i]<dp[q[r]]) r--;q[++r]=i; } if(!dp[L]) printf("-1"); //特例处理 else printf("%d",dp[L]); return 0; }
Review:
(1)还是要对特判细节想清楚啊……
(2)对于有决策单调性的题目想到单调性优化
(3)选择$dp$状态时要考虑状态集是否包含了所有“过程”中的“终点”
(如果按$cows' ranges$选取则不能)