CF-515div2-B-有趣的贪心(挑战的P46)- Heaters

题目传送门
题意:
在一个数列里面有0,1。0代表没有heater,1代表有heater.每个heater都有相同的半径r。要你打开最少的heater是的每个地方都能被heat到。
注意1本身也是没有heater的。所以要考虑1.昨天我就是这里理解错了,所以一直debug都没有改出来。正确理解题意是多么重要的一件事。现在终于该出来了,我感慨万千。debug的历史。
思路:
首先这与挑战P46的题目差不多。可以先去那里看一下。
首先,贪心从最右端(半径所在的范围)找heater,[pos+1,pos+r-1],从pos+r-1开始遍历到pos+1,如果有直接用,否则打开一个没有打开的1.如果这个方向不行。那么去左端找,[pos-r+1,pos-1],从pos-1开始遍历到pos-r+1.如果有已经打开的1,就进步下一步。否则注意了,如果你先在的中心点是1,那么前面都不行后(右端不行,左端没有现成的),那么就打开本身,而不是在左端新打开一个。如果是0的话,那只有在左端新打开一个了。
具体细节看代码注释。‘
这种题是一种套路,要么从左往右,要么从右往左,要么两者。
提供两个版本,第一个复杂长了点,第二个简化版。

AC code:
#include<iostream>
#include<cstdio>
#include<vector>
#include<bitset>
#include<stack>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
#include<fstream>
#include<cstdlib>
#include<algorithm>

using namespace std;

//#define pii pair<int, int>
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a,b) for(int i=a;i>=b;i--)
#define all(x) x.begin(),x.end()
#define PER(i,x) for(auto i=x.begin();i!=x.end();i++)
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
typedef long long LL;
typedef pair<int,int> pii;
const double eps=1.0e-5;
const int maxn=1e3 + 10;
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int n = 0,r = 0; 
int a[maxn],pos[maxn];
bool vis[maxn];
void solve(){
	fill(vis,vis+maxn,false);

	int ans = 0,fg = 0,flag = 0;
	for(int i = 1;i <= n;){
		fg = 0;
		for(int j = max(i-1,1);j >= max(i-r+1,1);--j){//首先遍历该点前面的点是否有heater 
			if(vis[j] == true){	//如果有,i++,重新开始 
				i++;
				fg = 1;
				break;
			}
		}
		if(fg == 1){
			continue;
		}
		for(int j = min(i+r-1,n);j >= i+1;--j){//然后从他的后面找heater,从最远的地方开始找	
			if(a[j] == 0 ){//0直接跳过 
				continue;
			}
			if(vis[j] == true){//找到一个heater,则跳到j+r 
				i = j + r-1+1;
				fg = 1;
				break;
			}
			if(vis[j] == false){//找到一个没开的1,那么就打开 
				ans++;
				vis[j] = true;
				i = j + r-1+1;
				fg = 1;
				break;
			}
		}
		if(fg == 1){
			continue;
		}
		//如果后半部分没有找到,就只有去前面打开1了 
		for(int j = max(i-1,1);j >= i-r+1 && j >= 1;--j){
			if(a[j] == 0){
				continue;
			}
			if(vis[j] == true){//找到一个已经打开的,就跳过 
				i = j + r-1+1;
				fg = 1;
				break;
			}
			if(vis[j] == false && a[i] != 1){//找到一个没开的1,但是本身不能是1,
			//因为本身是1的话 ,不如点开本身,就不开前面的1了 
				ans++;
				vis[j] = true;
				i = j + r-1+1;
				fg = 1;
				break;
			}
		}
		if(a[i] == 1 && fg == 0){//如果后面没有找到1,并且前面没有已经打开的1, 
			ans++;//那么就打开本身的1 
			vis[i] = true;
			i++;
			fg = 1;
			continue;
		}
		if(fg == 0){//如果都找不到,那么这里不能被heat 
			flag = 1;
			break;
		}
	}
	printf("%d\n",flag == 1 ? -1 : ans);
}
int main(){
	#ifndef ONLINE_JUDGE
		//freopen("a.txt","r",stdin);
	#endif
	while(~scanf("%d %d",&n,&r)){
		per(i,1,n){
			scanf("%d",&a[i]);
		} 
		solve();
	}
	
	return 0; 
}

简单版本:

AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int a[maxn];
int n = 0,r = 0;

int main(){
	while(~scanf("%d %d",&n,&r)){
		for(int i = 1;i <= n;++i){
			scanf("%d",&a[i]);
		}
		int fg = 0;
		int last = 1,pre = 1;
		int ans = 0;
		while(last <= n){
			int flag = -1;
			for(int j = max(last-r+1,1);j <= min(last+r-1,n);++j){
				if(a[j] == 1){
					flag = j;
				}
			}
			if(flag == -1){
				fg = 1;
				printf("-1\n");
				break;
			}
			ans++;
			last = flag + r ;
			 
			if(last == pre){
				last += 1;
			}
			pre = last;
		}
		if(fg == 1){
			continue;
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值