请问, John 最少需要安装多少个喷水头。
思路(参考郭炜老师课件):从线段的起点向终点安装喷水头,令f(X)表示:所安装喷水头的喷洒范围 恰好覆盖直线上的区间[0 X]时,最少需要多少个喷水头。显然,X应满足下列条件 1、X为偶数
2、X所在位置不会出现奶牛,即X不属于任何一个(S,E)
3、X≥2A
4、当X>2B时,存在Y∈[X-2B X-2A]且Y满足上述三个条件,使得 f(X)=f(Y)+1。由此得到
初始条件: f(X)=1: 2A≤X≤2B 、且X位于任何奶牛的活动范围之外;
状态转移方程:f(X)=1+min{f(Y): Y∈[X-2B X-2A]、Y位于任何奶牛的活动范围 之外}: X>2B。
关键在于快速找到[X-2B X-2A]中使得f(Y)最小的元素,有优先队列维护即可。注意stl优先队列用结构体类型的写法。
再有一点需要注意是如何快速判断一个点处是否有奶牛出没,而已在之前做一遍初始化,方法见代码。
#include <cstdio> #include <string> #include <queue> #include <iostream> #include <cstdlib> #include <cmath> #include <algorithm> #define INF 0x3fffffff using namespace std; #define N 1000005 #define INF 0x3fffffff struct node{ int x,f; node(int xx=0,int ff=0):x(xx),f(ff){} bool operator<(const node &x) const { return f>x.f; } }; priority_queue<node> q; int c[1005][2],dp[N],flag[N]; int n,m,a,b; int main(){ int i,k; memset(flag, 0, sizeof(flag)); scanf("%d %d %d %d",&n,&m,&a,&b); for(i = 0;i<n;i++){ scanf("%d %d",&c[i][0],&c[i][1]); flag[c[i][0]+1]++;//为了判断一个点处是否有奶牛出没 flag[c[i][1]]--; } for(i = k = 0;i<=m;i++){ k += flag[i]; flag[i] = k>0;//说明有奶牛出没 dp[i] = INF; } a <<= 1; b <<= 1; for(i = a;i<=b;i+=2) if(!flag[i]){//如果没有奶牛 dp[i] = 1; if(b+2-a >= i)//在求F[i]的时候,要确保队列里的点x<= i - a q.push(node(i,1)); } for(i = b+2;i<=m;i+=2){ if(!flag[i]){ while(!q.empty()){ struct node tmp = q.top(); if(tmp.x < i-b){//范围之外的点直接剔除 q.pop(); continue; } dp[i] = tmp.f+1; break; } } if(dp[i+2-a] != INF)//队列中增加一个可达下个点的点 q.push(node(i+2-a,dp[i+2-a])); } if(dp[m] == INF) printf("-1\n"); else printf("%d\n",dp[m]); return 0; }