HihoCoder - 1362 修补木桶(二分+动态规划+思维技巧)

题目大意:

有n个木桶片环形排列高矮不一,有m次机会,每次选择一个长度为L的连续区间将高度变成无限长。
数据范围限制:
n < 1000 n<1000
L < 20 L<20
m L < n m*L<n
求计算操作之后如果希望最短的木板尽量高,能有多高?

做法分析:

考虑二分答案,对于一个确定的答案x,我们将木板中长度大于等于x的标记为0,小于x的标记为1。那么现在问题就转化成了:对于一个01环形序列,我能否用m个长度为L的区间将其中的1完全覆盖(区间可以重叠)。

下面考虑该转化后的子问题:设dp(i)表示前i个点中的1完全被覆盖最少需要的区间数,那么:
对于标记为1的位置:dp(i) = min{dp(pre1(i-L)) ,dp(i-L),dp(i-L+1),…dp(i-1)}+1
对于标记为0的位置:dp(i) = dp(pre1(i));

这里需要将序列循环右移L-1次(共L种不同01序列),每次分别进行一次dp,然后得到答案。
时间复杂度: O ( l o g ( 2 e 9 ) L n L ) O(log(2e9)*L*n*L)

代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <stdlib.h>
using namespace std;
bool coverSeq01(const vector<int>& a, int m, int len) {
	vector <int> dp;
	/*printf("test:");
	printf("%d %d\n ",m,len);
	for (int i = 0; i < a.size(); i++) {
		printf("%d", a[i]);
	}
	printf("\n");*/
	int pre1 = -1;
	for (int i = 0; i < a.size(); i++) {
		if (a[i] == 0) {
			if (pre1 == -1) {
				dp.push_back(0);
			}
			else {
				dp.push_back( dp[pre1] );
			}
		}
		else {
			int temp;
			if (pre1 == -1) {
				temp = 0;
			}
			else {
				temp = dp[pre1];
			}
			for (int j = max(i - len, 0); j < i; j++) {
				temp = min(temp, dp[j]);
			}
			dp.push_back(temp + 1);
			pre1 = i;
		}
	}
	/*printf(" ");
	for (int i = 0; i < a.size(); i++) {
		printf("%d", dp[i]);
	}
	printf("\n");
	printf("ans:%d\n", dp[a.size() - 1] <= m);*/
	return dp[a.size() - 1] <= m;
}
bool testMid(int limit, const vector<int>& a, int m, int len) {
	for (int mov = 0; mov < len; mov++) {
		vector<int>s;
		for (int i = mov; i < a.size(); i++) {
			if (a[i] < limit) {
				s.push_back(1);
			}
			else {
				s.push_back(0);
			}
		}
		for (int i = 0; i < mov; i++) {
			if (a[i] < limit) {
				s.push_back(1);
			}
			else {
				s.push_back(0);
			}
		}
		if (coverSeq01(s, m, len) == true) {
			return true;
		}
	}
	return false;
}
int main()
{
	int n, m, L;
	while (cin >> n >> m >> L) {
		vector <int> a;
		int l = 2e9, r = 0;
		for (int i = 0; i < n; i++) {
			int temp = 0;
			cin >> temp;
			a.push_back(temp);
			l = min(l, temp), r = max(r, temp);
		}
		while (l <= r) {
			//cout << l << " " << r << endl;
			int mid = (l + r) / 2;
			if (testMid(mid, a, m, L) == true) {
				l = mid + 1;
			}
			else {
				r = mid - 1;
			}
		}
		cout << l - 1 << endl;
	}
}

发布了227 篇原创文章 · 获赞 142 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_36306833/article/details/102932273
今日推荐