算法提高 盾神與砝碼稱重(Dfs)

試題 算法提高 盾神與砝碼稱重

資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
  有一天,他在宿舍裏無意中發現了一個天平!這個天平很奇怪,有n個完好的砝碼,但是沒有遊碼。盾神爲他的發現興奮不已!於是他準備去稱一稱自己的東西。他準備好了m種物品去稱。神奇的是,盾神一早就知道這m種物品的重量,他現在是想看看這個天平能不能稱出這些物品出來。但是盾神稍微想了1秒鐘以後就覺得這個問題太無聊了,於是就丟給了你。
輸入格式
  第一行爲兩個數,n和m。
  第二行爲n個數,表示這n個砝碼的重量。
  第三行爲m個數,表示這m個物品的重量。
輸出格式
  輸出m行,對於第i行,如果第i個物品能被稱出,輸出YES否則輸出NO。
樣例輸入
4 2
1 2 4 8
15 16
樣例輸出
YES
NO
樣例輸入
4 1
10 7 1 19
6
樣例輸出
YES
數據規模和約定
  1<=n<=24, 1<=m<=10.
  
題解
  (假設物品放在天平左端)每個砝碼只有三種狀態:放在天平右端;放在天平左端;不使用。按照這種思路 最多要算 324=28*1010 看似會超時,很多情況及時剪枝回溯,另外,把砝碼按從大到小排序也可以加快計算,是可以AC的。
剪枝:如果剩下的砝碼都放在天平右端,都比左端輕,那麼不可能稱出來。
sum[i] 存儲第 i 到第 n 個砝碼的重量和。
AC代碼如下

#include<cstdio>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cstring>
using namespace std;

int n,m;
int sum[30];
int fama[30];
int wupin[20];

bool dfs(int left,int k){
	if(sum[k]<abs(left))	//剩下的砝碼都放到右端天平都不能平衡
		return false;
	if(left==0||abs(left)==sum[k])	//天平平衡了或者剩下的砝碼都放在右端能平衡
		return true;
	if(k==n)		//用滿了n個砝碼依然沒稱出來
		return false;

	if(dfs(left-fama[k],k+1))	//左物右碼 
		return true;
	if(dfs(left+fama[k],k+1))	//左物左碼 
		return true;	
	if(dfs(left,k+1))		//不放
		return true;
				
	return false;
}

int main(){
	int all=0;

	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&fama[i]);
		all+=fama[i];	
	}
	for(int j=0;j<m;j++)
		scanf("%d",&wupin[j]);
		
	sort(fama,fama+n,greater<int>());
	sum[0]=all;
	for(int i=1;i<n;i++){
		sum[i]=sum[i-1]-fama[i-1];
	}
	
	for(int i=0;i<m;i++){
		if(dfs(wupin[i],0))
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

PS.這裏也放一下錯誤的解法,就是每次從剩下的砝碼挑一個,然後只要考慮兩種情況,放在天平左邊或者右邊,但是複雜度比前面的大,因爲要 全排列砝碼,每次挑一個,而且會有重複的,先挑a再挑b,和先挑b再挑a是一樣的,所以這種做法一組測試也沒跑出來。

#include<cstdio>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cstring>
using namespace std;

int n,m;
bool vis[30];
int fama[30];
int wupin[20];

bool dfs(int left,int have){	//left是還需要的砝碼 have是還有的所以砝碼總重量
	if(abs(have)<abs(left))
		return false;
	if(left==0||abs(have)==abs(left))
		return true;
	for(int i=0;i<n;i++)
		if(!vis[i]){
			vis[i]=1;	
			if(dfs(left-fama[i],have-fama[i]))	//左物右碼 
				return true;
			if(dfs(left+fama[i],have-fama[i]))	//左物左碼 
				return true;	
			vis[i]=0;
		}
	return false;
}

int main(){
	int all=0;

	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&fama[i]);
		all+=fama[i];	
	}
	for(int j=0;j<m;j++)
		scanf("%d",&wupin[j]);
	sort(fama,fama+n,greater<int>());
	
	for(int i=0;i<m;i++){
		memset(vis,0,sizeof(vis));
		if(dfs(wupin[i],all))
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章