【藍橋杯 包子湊數】開啓dp揹包問題系列的大門(完全揹包)

標題:包子湊數

小明幾乎每天早晨都會在一家包子鋪喫早餐。他發現這家包子鋪有N種蒸籠,其中第i種蒸籠恰好能放Ai個包子。每種蒸籠都有非常多籠,可以認爲是無限籠。

每當有顧客想買X個包子,賣包子的大叔就會迅速選出若干籠包子來,使得這若干籠中恰好一共有X個包子。比如一共有3種蒸籠,分別能放3、4和5個包子。當顧客想買11個包子時,大叔就會選2籠3個的再加1籠5個的(也可能選出1籠3個的再加2籠4個的)。

當然有時包子大叔無論如何也湊不出顧客想買的數量。比如一共有3種蒸籠,分別能放4、5和6個包子。而顧客想買7個包子時,大叔就湊不出來了。

小明想知道一共有多少種數目是包子大叔湊不出來的。

輸入
----
第一行包含一個整數N。(1 <= N <= 100)
以下N行每行包含一個整數Ai。(1 <= Ai <= 100)  

輸出
----
一個整數代表答案。如果湊不出的數目有無限多個,輸出INF。

例如,
輸入:
2  
4  
5   
程序應該輸出:
6  
再例如,
輸入:
2  
4  
6    
程序應該輸出:
INF
樣例解釋:
對於樣例1,湊不出的數目包括:1, 2, 3, 6, 7, 11。  
對於樣例2,所有奇數都湊不出來,所以有無限多個。  

資源約定:
峯值內存消耗(含虛擬機) < 256M
CPU消耗  < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多餘內容。

所有代碼放在同一個源文件中,調試通過後,拷貝提交該源碼。
不要使用package語句。不要使用jdk1.7及以上版本的特性。
主類的名字必須是:Main,否則按無效代碼處理。
提交程序時,注意選擇所期望的語言類型和編譯器類型。


首先這道題基本上算是我第一次接觸揹包問題,那麼就有必要分享一下我對揹包問題的學習過程

首先感謝對我最有幫助的三篇文章(建議順序觀看)

http://www.cnblogs.com/fengziwei/p/7750849.html揹包問題:0-1揹包、完全揹包和多重揹包

https://blog.csdn.net/mu399/article/details/7722810動態規劃之01揹包問題(最易理解的講解)

https://blog.csdn.net/qq_40828060/article/details/78422412關於01揹包逆序遍歷容積的思考

第一篇博客是對揹包問題的主要闡述

關於第二篇博客是對01揹包問題的側重闡述,其中的表格(如下)一定要認真思考,從二維到一維的轉變,在我認爲則是

找到了這樣一個圖:

這個表格對於理解0-1揹包問題很有用,我們利用它來理解一下爲什麼要把第二層循環顛倒這個問題。考慮d9這一項,要求出這個狀態,我們有可能利用到的就是e1到e8這8個狀態,當我們把第二層循環顛倒過來時,當我們要求f[j]時,f[j -1]到f[1]還保存着下面一行的狀態,因此可以採用一維數組解決。我們可以考慮一下假如不把第二層循環顛倒,當要求f[j]時,f[j - 1]到f[1]已經是同一行的狀態了,根本沒法求

其次是第三篇博客,這篇博客讓我理解完全揹包內層循環正序和01揹包內層循環逆序的原因。

最後附上這道題的代碼:

import java.util.Scanner;

public class 包子湊數_8_完全揹包1 {
	static int bun[] = new int [110];
	static int maxn = 12000;
	static int dp[] = new int [maxn];
	static int count;
	static int gcd(int a,int b)//輾轉相除法求最大公約數,如果返回1,說明兩個數互質,互質的兩個數肯定能湊出其他數
	{
		return b==0?a:gcd(b,a%b);
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int N;//N種蒸籠
		Scanner in = new Scanner(System.in);
		N = in.nextInt();
		for(int i=1;i<=N;i++)
		{
			bun[i]=in.nextInt();
		}
		int k = bun[1];//第一個包子數拿出來
		boolean flag = false;
		for(int i=2;i<=N;i++)
		{
			k = gcd(k,bun[i]);//兩兩比較
			if(k==1)
			{
				flag = true;//說明存在互質的數
			}
		}
		if(flag==false)//走完循環仍然沒有互質的數,說明有無限個湊不出的數
		{
			System.out.println("INF");
			return;
		}
		else//存在有限個湊不出的數,化爲完全揹包問題求解
		{
			dp[0]=1;//容量爲0,能湊出標記爲1
			for(int i=1;i<=N;i++)
			{
				for(int j=bun[i];j<maxn;j++)
				//for(int j=maxn;j>=bun[i];j--) 注意01揹包的內層循環是逆序
				{
					dp[j] = Math.max(dp[j], dp[j-bun[i]]);
				}
			}
		}
		//如果剛好裝滿了,dp[j-bun[i]]=1,因爲j-bun[i]是揹包的剩餘容量,剩餘容量=0,即dp[0]=1;
		for(int i=0;i<dp.length;i++)
		{
			if(dp[i]!=1)//說明沒裝滿
			{
				count++;
			}
		}
		System.out.println(count);
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章