题目传送门
题意:
在一个数列里面有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;
}