POJ 1014——Dividing(深搜+剪枝)

題目鏈接:http://poj.org/problem?id=1014點擊打開鏈接

簡單來說就是在一堆數中能否找到一組組合使它們的值等於所有數的值的一般,所以第一個條件就是所有數的和應能被整除,看到這題首先想到的就是揹包,看了評論大多數人都用的多重揹包,因爲不熟悉多重揹包,只能用深搜去碰運氣,結果貢獻了好多次超時,深搜在這題來說就是不斷遞歸尋找一種組合能夠使它們的總值達到要求,找到則遞歸結束,輸出答案,直到找完所有組合還未找到則沒有這種組合。數的數目小的話是很簡便的,但是數量一旦過千甚至過百,龐大的遞歸規模必定超時,這時就需要剪枝來減小規模了,這個剪枝想法也是參考了討論區裏一位名字是fqaq的評論,貼上其想法:

考慮值爲6的石頭:
將值爲6的石頭分爲兩部分,一部分均分爲兩堆,另一部分用於填補缺口。
所謂填缺口就是當其他的石頭分完之後再把這部分石頭分入兩堆(也有可能只放入一堆)。
現在考慮需要多少6值石頭用於填缺口。
假設左邊的總值小於右邊的總值
1.要能均分,剩下的缺口一定是6的倍數;
2.在分配其他的石頭時,可以做到儘可能的是缺口小
(比如,如果可以在右邊找到幾個石頭使它們的值之和爲6,可以把它們勻給左邊);
3.當不能再從右邊勻給左邊時,可能出現的缺口在大是多少?(缺口大小確定了就可以確定需要的用於填缺口的石頭數量)
        缺口可能是30(右邊剛好比左邊多6個5值石頭)
        缺口可能是是36? 
        {要能產生36的缺口,需要在右邊從值爲1-5的石頭中拿出幾個使它們的總值不小於36,並且這幾個石頭勻不過去。
         2值石頭不能超過2個(超過了可以勻一個6過去),3值石頭不能超過1個(超過了可以勻一個6到左邊),4值石頭不能超過2個(超過了可以勻一個12到左邊),5值石頭不能超過5(超過了可以勻一個30到左邊,缺口轉移到右邊爲24<36),1值石頭不能超過5個。
                1*5+2*2+3*1+4*2=20 36-20=16,5值石頭至少需要3個;
                5值石頭有3個:4值石頭只能有1個(4+4+5+5=18 可以勻一個18到左邊),不能有3值石頭(3+5+5+5=18),湊不齊;
                5值石頭有4個: 同樣湊不齊;
                5值石頭有5個: 同樣湊不齊。
      }        
能得到的最小缺口不可能是36,因爲總能勻幾個石頭使缺口小於36。

所以對於6值石頭,最多需要5個來填缺口,其餘均分即可。

對於其他值的石頭,可以採用同樣的分析;
值              需要用於填缺口的數量
1               6
2               5
3               5
4               5
5               6
6               5

統一一下
1               6
2               6
3               6
4               6
5               6
6               6

留下需要填補缺口的石頭,把剩下的石頭(偶數個)均分爲兩堆
需要考慮的也只是用來填缺口的石頭。

if(marbles[i]>6){
        if(marbles[i]%2)
                marbles[i]=5;
        else marbles[i]=6;
}


多餘的值根本沒有意義,就像消消樂那樣,只要去掉那些能自己對消掉,留下至少能填補其他缺口的就行,6這個值說實話我至今不太明白,我原本的想法是取1,2,3,4,5,6的公倍數,留下至少60應該就可以了,也許是我考慮不太周全,結果是錯的,上面貼的那些想法按我自己理解,應該是取最少的需要留下填補缺口的數量,2和3互補至少要留3個,3和4互補至少要留4個,4和5互補至少要留5個,5和6互補至少要留6個,1和6也是6個,綜上,要留出足夠的彈珠能夠填補缺口的需要。可能解釋得不太清楚,大家可以去poj翻翻這個題目的討論區


AC代碼:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int a[7];
int dfs(int value)
{
	if(value==0){
		return 1;
	}
	int i;
	for(i=7;i>0;i--){
		if(a[i]==0||value-i<0){
			continue;
		}
		a[i]--;
		if(dfs(value-i)==1){
			return 1;
		}
		a[i]++;
	} 
	return 0;
}
int main()
{
	int i;
	int n;
	int sum;
	n=1;
	//freopen("d://in.txt","r",stdin);
	for(i=1,sum=0;i<7;i++){
		scanf("%d",&a[i]);
		if(a[i]>6){
                    if(a[i]%2)
                        a[i]=5;
                    else a[i]=6;
                }
		sum=sum+a[i]*i;
	}
	while(sum!=0){
		if(sum%2!=0){
			printf("Collection #%d:\nCan't be divided.\n\n",n++);
		}
		else{
			if(dfs(sum/2)==1){
				printf("Collection #%d:\nCan be divided.\n\n",n++);
			}
			else{
				printf("Collection #%d:\nCan't be divided.\n\n",n++);
			}
		}
		for(i=1,sum=0;i<7;i++){
		    scanf("%d",&a[i]);
		    if(a[i]>6){
                        if(a[i]%2)
                            a[i]=5;
                        else a[i]=6;
                    }
		    sum=sum+a[i]*i;
	    }
	}
	return 0;
}

發佈了58 篇原創文章 · 獲贊 22 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章