1. 程式人生 > >CF-515div2-B-有趣的貪心(挑戰的P46)- Heaters

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;
}