题目链接: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原创,但可以转载,因为我们是兄弟。