题目链接:http://poj.org/problem?id=2373
题目大意:给定一个长度L,等分为L段,有n头牛,每头牛有一个活动区域,比如3,6,又有喷泉,喷的是圆形,半径在A,B之间,要求在长度为L的区间内安放最少的喷泉,使得每个位置都能被喷到,有两个要求:1、牛活动地区域内必须只由一个喷泉喷到,如果活动区域是(3,6),A = B = 1,则最大半径为1,如果放在3,则只能喷到(3,5),如果放在5,只能喷到(4,6),则不合法。 2、喷泉不能喷到小于0或者大于L的地方..
解题思路:很明显,这是道DP题,繁琐的DP题。这里设dp[i]为正好喷到i这个位置缩安放的最少喷泉数,最后的答案dp[L]肯定是从dp[L-2*B] --dp[L-2*A]转移而来,再往前推..直到dp[0],牛不可能在o这个位置活动,所以初始化dp[0]为0.易得状态转移方程为:dp[i] = min(dp[k)] + 1 (i - 2 * B <= L <= i - 2 * A)。
得到了状态转移方程很容易想到o(L^2)的解法,不断从前面转移。但是L是100 0000,100万*100万 = 1万亿,顿时觉得自己太天真了。于是想到单调队列,单调队列可以在O(1)的时间内找到最小或者最大的符合条件的值,有了这个进行优化,复杂度问题就迎刃而解了。剩下来的就是一些细节问题,如怎么处理使的每个牛活动地区域只有一个喷?只要禁止从牛活动的区域内安放喷泉,不从里面转移就ok了。 再如奇数位置可以放喷泉吗?当然不可以,如果某个奇位置安放了喷泉,而喷泉的直径又是偶数,则最后的情况必定是某个喷泉喷到小于0或者大于L的地方。
如果还不懂,倒立思考五分钟,或者看我的代码注释。
测试数据:
2 8
1 2
6 7
3 6
1 6
1 1
0 4
1 6
3 4
0 4
代码:
#include <stdio.h> #include <string.h> #define MAX 1000010 #define INF 2147480000 int L,n,A,B; int head,tail,ok[MAX]; //ok[i] = 1 代表可以安放喷泉 = 0代表牛活动区域没办法安放 struct node { //代表牛活动地区域 int beg,end; }cow[1100]; struct dpnode { //因为单调队列中需要用到位置,所以多设了个结构体, int in,num; //in 下标,num为这之前安放的喷泉数 }dp[MAX],qu[MAX]; void Solve() { int i,j,k,tp; for (i = 1; i <= n; ++i) for (j = cow[i].beg + 1; j < cow[i].end; ++j) ok[j] = 0; //每只牛活动的区域内不能安插两个碰头 dp[0].num = 0,head = 1,tail = 1; //初始化操作 for (i = 2 * A; i <= L; ++i) { //单调队列操作 tp = i - 2 * A; //对于i最多从tp位置转移 while (head > tail && qu[head-1].num >= dp[tp].num) head--; //每次都从队头取走比dp[tp]弱的节点 if (ok[tp]) qu[head++] = dp[tp]; //如果这点可以设置喷泉,就入队列 if (i % 2 == 1 || ok[i] == 0) continue;//不能再奇位置或牛活动地区域放喷泉 while (head > tail && qu[tail].in < i - 2 * B) tail++; //取走坐标小于i - 2 * B的结点 if (head > tail) dp[i].num = qu[tail].num + 1; //状态转移 } } int main() { int i,j,k,flag = 1; while (scanf("%d%d",&n,&L) != EOF) { scanf("%d%d",&A,&B); for (i = 1; i <= n; ++i) { scanf("%d%d",&cow[i].beg,&cow[i].end); if (cow[i].end - cow[i].beg > 2 * B) flag = 0; //这种情况必须在牛活动区域内安放两个以上喷头 } if (flag == 1) { for (i = 0; i <= L; ++i)//初始操作 ok[i] = 1,dp[i].num = INF,dp[i].in = i; Solve(); } if (dp[L].num >= INF || !flag) printf("-1\n"); else printf("%d\n",dp[L].num); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。