poj 2373 Dividing the Path (DP_单调队列优化)

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值