Codeforces 15D Map 单调队列+构造

36 篇文章 0 订阅
11 篇文章 0 订阅

题目链接:http://codeforces.com/problemset/problem/15/D

题意:给定n*m的矩阵,每个点都是一个地基,上面的数字表示该地基的高度。

再给定a*b的小房子,要把a*b放在这个矩阵上,显然建房子要保证选取的地基高度一致。

若不一致,则要把选取的a*b大的矩阵中所有地基都挖低使得和其中一块最矮的地基高度一样,花费是挖的高度和

操作:

选取当前花费最小的一块矩阵,在上面建房子,若有多个方案花费一样,则优先选左上角的。

问:

建的房子的左上角坐标和建该房子的花费

思路:

显然我们要先求出选取每个点时,这个点的地基。用单调队列求一次行的,再在这基础上求一次列的就能得到这个点的地基。

然后把所有点都扔到堆里,跑出解即可。


双端队列的简单用法:

deque<int>q;

q.empty();

q.pop_front(); q.pop_back();

q.push_back(i); q.push_front(i);

单调队列:队尾->队首,队列中存的不是数组的元素,而是数组的下标

单调队列求最小就用递增,对于某一个数要求出这个数u到u+len-1的长度内的最小值:

对于i点,则要求出i点的最小值,我们不能直接求[ i, i+len-1]的值,而是求出[i-len+1, i]的最小值,然后转移到上述的 [u,u+len-1] 上

继续讨论对于i点在单调队列中的求解

1、则队尾元素必须是 [i-len+1,i]区间上的,所以 q.back()>=i-len+1,即->当{ q.back()+len-1<i } 时要把q.back()弹掉

2、要保持单调递增,所以当把这个元素x[i]加入队列时, q.front()必须<=x[i], 所以当 { q.front()>x[i] } 时要把q.front()弹掉

3、此时加入x[i],则此队列的数都在 [i-len+1,i]区间上且元素都是单调递增的,最小的元素是 x[q.back()]

4、显然x[i]的最小值就是 x[q.back()] ,到此计算出x[i]的最小值了

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<set>
#include<vector>
#include<map>
#include<math.h>
#include<queue>
#include<string>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define N 1015
#define LL __int64
#define ll int
ll x[N], dou[N];
void work(ll len, ll top){ 
	deque<ll>q;// q.front() -> q.back()
	for(ll i = 1; i <= top; i++) {
		while(!q.empty() && q.front() + len <= i)q.pop_front();
		while(!q.empty() && x[i] <= x[q.back()]) q.pop_back();
		q.push_back(i);
		dou[i] = x[q.front()];
	}
}
ll n, m, a, b;
struct node{
	ll x,y;
	LL val;
	node(ll a1=0,ll a2=0,LL a3=0):x(a1),y(a2),val(a3){}
	bool operator<(const node& Node) const{
		if(Node.val==val)
		{
			if(Node.x==x) return Node.y<y;
			else return Node.x<x;
		}
		else return Node.val<val;
	}
};
priority_queue<node>q;
vector<node>ans;
bool use[N][N];
void go(){
	ans.clear();
	while(!q.empty()){
		node u =q.top(); q.pop();
		if(use[u.x][u.y] || use[u.x+a-1][u.y+b-1] || use[u.x][u.y+b-1] || use[u.x+a-1][u.y])continue;
		ans.push_back(u);
		for(ll i = u.x; i < u.x+a; i++)
			for(ll j = u.y; j < u.y+b; j++)
				use[i][j] = 1;
	}
	ll siz = ans.size();
	printf("%d\n",siz);
	for(ll i = 0; i < siz; i++) printf("%d %d %I64d\n",ans[i].x,ans[i].y,ans[i].val);
}
ll val[N][N], mp[N][N];
LL sum[N][N];
int main(){
	ll i,j;
	while(~scanf("%d %d %d %d",&n,&m,&a,&b)) {
		for(i=1;i<=n;i++)for(j=1;j<=m;j++)
		{
			scanf("%d",&mp[i][j]);
			sum[i][j] = (LL)mp[i][j] + sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
		}
		for(i=1;i<=n;i++) {
			memcpy(x, mp[i], sizeof x);
			work(b, m);
			for(j=1;j<=m;j++) val[i][j] = dou[j];
		}
		for(i=b;i<=m;i++) {
			for(j=1;j<=n;j++) x[j] = val[j][i];
			work(a, n);
			for(j=1;j<=n;j++) val[j][i] = dou[j];
		}
		for(i=a; i<=n; i++)
			for(j=b;j<=m;j++)
				q.push(node(i-a+1,j-b+1,sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b]-(LL)a*(LL)b*(LL)val[i][j]));
		go();
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值