1. 程式人生 > 實用技巧 >洛谷 P2101 命運石之門的選擇 (分治)

洛谷 P2101 命運石之門的選擇 (分治)

P2101 命運石之門的選擇 (分治)

介紹

El Psy Congroo

題目連結

沒錯,作為石頭門廚,怎麼能不做石頭門的題呢?(在搜石頭門的時

候搜到了本題)

本題作為一道分治基礎練習題還是不錯的,雖然看起來挺簡單,但還

是有不少需要思考的地方的。(可能是我太菜了)

分析

我們對本題進行分析,

就拿下面這個圖舉例

我們首先觀察到了紅色部分,紅色部分是當前所能構成的最大矩形

我們擁有兩種塗色方法,橫著塗和豎著圖,因為塗一次色的代價與塗

色面積無關,所以我們每一次塗色需要儘可能的多塗。

對於紅色部分,顯然,全部採用同一種塗色方法是要比兩

種方法同時採取更優的,因為當我們混用塗色方法時,一定是可以

通過去掉某一次塗色來降低所需代價的。

針對紅色部分,如果我們全部採用豎著塗,因為我們要儘可能的多

塗,所以我們既然可以豎著塗完紅色部分,也可以在同代價下塗完

整個圖,所以我們目前塗完整個圖的代價就是當前圖形的寬度,如

果我們採用橫著圖,塗完整張的總代價就是(該圖形中最低的小矩

形的高度)+(塗完紅色部分以外部分的最小代價)

我們所要求的答案就是這兩種方法的代價最小值

那如何求出塗完紅色部分以外部分的最小代價呢?

這時候就要採用分治思想了,

我們用原圖形減去紅色部分,得到了一個或幾個圖形,我們目前要

求的就是塗完所有新圖形的最小代價,我們針對每一個新圖形都按

先前求原圖形的最小代價的方法處理,最後將其合併即可。

放一下程式碼

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#define int long long
using namespace std;
const int maxn=1e4;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')	
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return ret*f;
}
int n;
int m;
int a[maxn];
int slove(int l,int r){
	if(l==r){
		return 1;//邊界
	}
	int t=r-l+1;//目前圖形的寬度
	int minn=0x3f3f3f3f;
	for(int i=l;i<=r;i++){
		if(a[i]<minn){
			minn=a[i];//找到最低矩形的高度
		}
	}
	int ans=minn;
	for(int i=l;i<=r;i++){
		a[i]-=minn;//減掉紅色部分
	}
	int ll=l;
	for(int i=l;i<=r;i++){
		if(a[i]&&!a[i-1])
			ll=i;
		if(a[i]&&(!a[i+1]||i==r)){
			ans+=slove(ll,i);//分治處理
		}
	}
	return min(ans,t);
}
signed main(){
//	freopen("a.txt","r",stdin);
	n=read(); 
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	cout<<slove(1,n); 
	return 0;
} 

這一切都是命運石之門的選擇