1. 程式人生 > 實用技巧 >竟然可以這樣學python!

竟然可以這樣學python!

一道不錯的剪枝題,用到的剪枝優化比較多,剪枝思路也值得學習

在這裡我只採取了能通過此題的剪枝,似乎還可以繼續優化?

剪枝講解在註釋裡

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=70;
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 f*ret;
}
int n;
int a[maxn];
int fla[maxn];
int vis[maxn];
int last;
int x;
bool cmp(int x,int y){
	return x>y;
}
inline bool dfs(int nu,int len,int ls,int t){
	if(nu>x){
		return true;//達到了我們所需的原木棍數,所以該劃分方法正確 
	}
	if(len==t){
		return dfs(nu+1,0,1,t);//當前狀態符合題意,進入下一狀態 
	}
	last=0;
	/*
		考慮到我們為什麼要從ls開始列舉?
		la是該木棒上一個列舉的木棍的下標,因為木棍陣列是降序排序的
		具有單調性,ls是個較大的值,在ls前的數一定是大於la的數
		我們已知,上一個狀態的len+a[ls]=目前的len,所以,a[ls]是<=t-len(上一狀態)&&符合題意的最大值
		所以當前狀態直接由ls開始列舉即可 
	*/ 
	for(int i=ls;i<=n;i++){
		if(!vis[i]&&len+a[i]<=t&&last!=a[i]){
			/*
			這裡同樣存在剪枝last為上個不符合狀態的值
			在上個狀態選擇它不能得到正確答案,當前狀態的值假設和它相同,同樣也找不到正確值 
			*/ 
			vis[i]=1;
			if(dfs(nu,len+a[i],i,t))
				return true;
			
				vis[i]=0;
				last=a[i];
			if(t-len==a[i]) return false;
			/*此步剪枝比較重要
			此步的意思就是當該木棒需要拼接的長度和當前木棍長度相等時卻拼接失敗
			直接返回失敗
			我們為什麼要這樣剪枝?
			當剩餘長度等於當前列舉長度時,把該木棍接到木棒上是最優的
			為什麼這樣最優?
			假設剩餘的木棍中也可以拼成剩餘長度,那用到的木棍一定多個長度小於剩餘長度的木棍
			用多個長度較小木棍來拼接對之後的拼接會產生較大的影響,但直接用一根是沒有影響的,所以
			如果無法直接用一根木棍拼接,
			剩餘木棍一定也不行
			所以直接返回錯誤 
			*/
			if(len==0){
				return false;
				/*
				len=0說明在列舉一個新的木棍,我們已知,假設剩餘的木棍可以組成木棒,那麼當前位置無論放剩下的哪一個也一定可以
				組成一組新的木棒,所以當無法構成新木棒時直接return ; 
				*/ 
			}
		}
	}
	return false;
}
/*
剪枝和dfs都非常巧妙
將目標狀態分為兩個部分
1 湊齊當前木棍
2 還原所有木棍
第二個狀態由1 組成 
*/ 
int main(){
	freopen("a.in","r",stdin);
	n=read();
	int tot;
	int l=0;
	int r=0;
	for(int i=1;i<=n;i++){
		x=read();
		if(x>50)
			continue;
		tot++;
		a[tot]=x;
		l=max(x,l);
		r+=x;
	}
	n=tot;
	sort(a+1,a+1+n,cmp);
	memset(vis,0,sizeof(vis));
	for(int i=l;i<=r;i++){
		if(r%i){
			continue;
		}
		x=r/i;
		if(dfs(1,0,1,i)){
			cout<<i;
			break;
		}
	}
	return 0;
}