2013年第四屆藍橋杯省賽試題及詳解(Java本科C組)

一葉之修發表於2019-02-28

藍橋杯歷年真題題目及題解目錄彙總 

 

  1. 結果填空 (滿分3分)
  2. 結果填空 (滿分5分)
  3. 結果填空 (滿分8分)
  4. 結果填空 (滿分12分)
  5. 程式碼填空 (滿分6分)
  6. 程式碼填空 (滿分11分)
  7. 程式設計(滿分4分)
  8. 程式設計(滿分12分)
  9. 程式設計(滿分15分)
  10. 程式設計(滿分24分)

 

 

1.標題: 猜年齡


    美國數學家維納(N.Wiener)智力早熟,11歲就上了大學。他曾在1935~1936年應邀來中國清華大學講學。

    一次,他參加某個重要會議,年輕的臉孔引人注目。於是有人詢問他的年齡,他回答說:

    “我年齡的立方是個4位數。我年齡的4次方是個6位數。這10個數字正好包含了從0到9這10個數字,每個都恰好出現1次。”

    請你推算一下,他當時到底有多年輕。

    通過瀏覽器,直接提交他那時的年齡數字。
    注意:不要提交解答過程,或其它的說明文字。

18,這題完全不用把0-9這個限制條件敲到程式碼上,光是4位6位就篩剩幾個數了,列印出來觀察更快

public class _01_猜年齡1 {

	public static void main(String[] args) {
		for(int i=11;i<=150;i++) {
			int a = i*i*i;
			int b = i*i*i*i;
			if(a>=1000 && a<=9999 && b>=100000 && b<=999999)
				System.out.println(i+" "+a+" "+b);
		}
	}

}


2.標題: 組素數

    素數就是不能再進行等分的數。比如:2 3 5 7 11 等。
    9 = 3 * 3 說明它可以3等分,因而不是素數。

    我們國家在1949年建國。如果只給你 1 9 4 9 這4個數字卡片,可以隨意擺放它們的先後順序(但卡片不能倒著擺放啊,我們不是在腦筋急轉彎!),那麼,你能組成多少個4位的素數呢?

    比如:1949,4919 都符合要求。


請你提交:能組成的4位素數的個數,不要羅列這些素數!!

注意:不要提交解答過程,或其它的輔助說明文字。

我覺得可以先人工列舉全排,4!/2!=12個吧,比你寫個全排函式快一些,而且普通的全排函式面對重複數字不能篩重,得出的是4!,所以人工完了之後就求素數個數吧答案:6 (1499,1949,4919,9419,9491,9941)

public class _02_組素數1 {

	public static void main(String[] args) {
		int[] a = new int[] {1499,1949,1994,9149,9194,9914,4199,4919,4991,9419,9491,9941};
		int ans=0;
		for(int i=0;i<a.length;i++) {
			boolean flag = true;
			for(int j=2;j*j<=a[i];j++)
				if(a[i]%j==0) {
					flag = false;
					break;
				}
			if(flag) {
				ans++;
				System.out.println(a[i]);
			}
		}
		System.out.println(ans);
	}

}

也貼一下通過全排列的做法吧,多個步雜湊查重

import java.util.HashSet;
import java.util.Set;

//素數判定、素數生成(篩法)、質因數分解
//有重複元素的全排列+檢查
public class _02組素數 {

  /**
   * 處理從k開始的排列
   * @param arr
   * @param k
   */
  static void f(int[] arr, int k) {
    if (k == 4)//前面都已確定
      check(arr);

    for (int i = k; i < 4; i++) {
      //交換
      int t = arr[k];
      arr[k] = arr[i];
      arr[i] = t;

      f(arr, k + 1);

      t = arr[k];
      arr[k] = arr[i];
      arr[i] = t;
    }
  }

  static Set<Integer> set = new HashSet<Integer>();

  private static void check(int[] arr) {
    int x = arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3];
    boolean flag = true;
    for (int i = 2; i <= Math.sqrt(x); i++) {
      if (x % i == 0) {
        flag = false;
        System.out.println(x+" false");
        break;
      }
    }
    if (flag) {
      set.add(x);
      System.out.println(x+" true");
    }
  }

  public static void main(String[] args) {
    int[] arr = {1, 4, 9, 9};
    f(arr, 0);
    System.out.println(set.size());
  }
}


3.標題: 馬虎的算式


    小明是個急性子,上小學的時候經常把老師寫在黑板上的題目抄錯了。

    有一次,老師出的題目是:36 x 495 = ?

    他卻給抄成了:396 x 45 = ?

    但結果卻很戲劇性,他的答案竟然是對的!!

    因為 36 * 495 = 396 * 45 = 17820

    類似這樣的巧合情況可能還有很多,比如:27 * 594 = 297 * 54

    假設 a b c d e 代表1~9不同的5個數字(注意是各不相同的數字,且不含0)

    能滿足形如: ab * cde = adb * ce 這樣的算式一共有多少種呢?


請你利用計算機的優勢尋找所有的可能,並回答不同算式的種類數。

滿足乘法交換律的算式計為不同的種類,所以答案肯定是個偶數。


答案直接通過瀏覽器提交。
注意:只提交一個表示最終統計種類數的數字,不要提交解答過程或其它多餘的內容。

142,暴力到沒朋友,填空題暴力列舉就好

import java.util.Scanner;
 
public class _02_馬虎的算式1 {
 
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int ans=0;
		for(int a=1;a<=9;a++)
			for(int b=1;b<=9;b++) {
				if(b==a)
					continue;
				for(int c=1;c<=9;c++) {
					if(c==a || c==b)
						continue;
					for(int d=1;d<=9;d++) {
						if(d==a || d==b || d==c)
							continue;
						for(int e=1;e<=9;e++) {
							if(e==a || e==b || e==c || e==d)
								continue;
							if((10*a+b)*(100*c+10*d+e)==(100*a+10*d+b)*(10*c+e))//ab * cde = adb * ce
								ans++;
						}			
					}
				}
			}
		
		System.out.println(ans);	
	}
}

 


4.標題: 第39級臺階

    小明剛剛看完電影《第39級臺階》,離開電影院的時候,他數了數禮堂前的臺階數,恰好是39級!

    站在臺階前,他突然又想著一個問題:

    如果我每一步只能邁上1個或2個臺階。先邁左腳,然後左右交替,最後一步是邁右腳,也就是說一共要走偶數步。那麼,上完39級臺階,有多少種不同的上法呢?


    請你利用計算機的優勢,幫助小明尋找答案。

要求提交的是一個整數。
注意:不要提交解答過程,或其它的輔助說明文字。

答案:51167078,一定要走偶數步?那不如一次就遞迴2步把,2步合成一步

public class _04第39級臺階1 {

	public static void main(String[] args) {
		System.out.println(f(39));
	}
	
	static int f(int x) {
		if(x==0)
			return 1;
		if(x<0)
			return 0;
		return f(x-2)+2*f(x-3)+f(x-4);//左腳1步右腳2步和右腳1步左腳2步不一樣的哦
	}

}

 


5.標題:有理數類

    有理數就是可以表示為兩個整數的比值的數字。一般情況下,我們用近似的小數表示。但有些時候,不允許出現誤差,必須用兩個整數來表示一個有理數。

    這時,我們可以建立一個“有理數類”,下面的程式碼初步實現了這個目標。為了簡明,它只提供了加法和乘法運算。

class Rational
{
    private long ra;
    private long rb;
    
    private long gcd(long a, long b){
        if(b==0) return a;
        return gcd(b,a%b);
    }
    public Rational(long a, long b){
        ra = a;
        rb = b;    
        long k = gcd(ra,rb);
        if(k>1){ //需要約分
            ra /= k;  
            rb /= k;
        }
    }
    // 加法
    public Rational add(Rational x){
        return ________________________________________;  //填空位置
    }
    // 乘法
    public Rational mul(Rational x){
        return new Rational(ra*x.ra, rb*x.rb);
    }
    public String toString(){
        if(rb==1) return "" + ra;
        return ra + "/" + rb;
    }
}

使用該類的示例:
    Rational a = new Rational(1,3);
    Rational b = new Rational(1,6);
    Rational c = a.add(b);
    System.out.println(a + "+" + b + "=" + c);


請分析程式碼邏輯,並推測劃線處的程式碼,通過網頁提交
注意:僅把缺少的程式碼作為答案,千萬不要填寫多餘的程式碼、符號或說明文字!!

數學通分:a/b + c/d = (a*d+b*c)/(b*d)

  • new Rational(ra*x.rb+rb*x.ra, rb*x.rb)
  • new Rational(this.ra * x.rb + x.ra * this.rb, this.rb * x.rb)

上面的答案都行,不要this是觀察到乘法函式也沒寫

 


6.標題:逆波蘭表示式

    正常的表示式稱為中綴表示式,運算子在中間,主要是給人閱讀的,機器求解並不方便。

    例如:3 + 5 * (2 + 6) - 1

    而且,常常需要用括號來改變運算次序。

    相反,如果使用逆波蘭表示式(字首表示式)表示,上面的算式則表示為:

    - + 3 * 5 + 2 6 1

    不再需要括號,機器可以用遞迴的方法很方便地求解。

    為了簡便,我們假設:

    1. 只有 + - * 三種運算子
    2. 每個運算數都是一個小於10的非負整數
    
    下面的程式對一個逆波蘭表示串進行求值。
    其返回值為一個陣列:其中第一元素表示求值結果,第二個元素表示它已解析的字元數。

 

    static int[] evaluate(String x)
    {
        if(x.length()==0) return new int[] {0,0};
        
        char c = x.charAt(0);
        if(c>='0' && c<='9') return new int[] {c-'0',1};
        
        int[] v1 = evaluate(x.substring(1));
        int[] v2 = __________________________________________;  //填空位置
        
        int v = Integer.MAX_VALUE;
        if(c=='+') v = v1[0] + v2[0];
        if(c=='*') v = v1[0] * v2[0];
        if(c=='-') v = v1[0] - v2[0];
        
        return new int[] {v,1+v1[1]+v2[1]};
    }


    

請分析程式碼邏輯,並推測劃線處的程式碼,通過網頁提交。
注意:僅把缺少的程式碼作為答案,千萬不要填寫多餘的程式碼、符號或說明文字!!

evaluate(x.substring(1+v1[1])),不會就試到會?樣例自己弄簡單的套

結構應該是這樣的:

(操作符佔的長度,如果從大的整體看一次長度只能為1)(v1佔的長度)(剩下不就是v2佔的長度,為v1+1剩下部分)

 


7.標題:核桃的數量

    小張是軟體專案經理,他帶領3個開發組。工期緊,今天都在加班呢。為鼓舞士氣,小張打算給每個組發一袋核桃(據傳言能補腦)。他的要求是:

    1. 各組的核桃數量必須相同
    2. 各組內必須能平分核桃(當然是不能打碎的)
    3. 儘量提供滿足1,2條件的最小數量(節約鬧革命嘛)

程式從標準輸入讀入:
a b c
a,b,c都是正整數,表示每個組正在加班的人數,用空格分開(a,b,c<30)

程式輸出:
一個正整數,表示每袋核桃的數量。

例如:
使用者輸入:
2 4 5

程式輸出:
20

再例如:
使用者輸入:
3 1 1

程式輸出:
3

資源約定:
峰值記憶體消耗(含虛擬機器) < 64M
CPU消耗  < 1000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。

  題目的意思是求3個數的最小公倍數,瞭解下簡單數論,擴充套件歐幾里得,同餘定理,
對於gcd,lcm 都可以通過先寫2個數的,在套用2個數的求n個數的,和數學的思想一致,先求出2個數的最小公倍數再求3個數的

import java.util.Scanner;

public class _07核桃的數量1 {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		System.out.println(lcm(in.nextInt(),in.nextInt(),in.nextInt()));
	}
	
	static int gcd(int a,int b) {
		if(b==0)
			return a;
		return gcd(b,a%b);
	}
	
	static int gcd(int a,int b,int c) {
		return gcd(gcd(a,b),c);
	}
	
	static int lcm(int a,int b) {
		return a*b/gcd(a,b);
	}
	
	static int lcm(int a,int b,int c) {
		int s = lcm(a,b);
		return s*c/gcd(s,c);
	}

}

 


8.標題:列印十字圖

    小明為某機構設計了一個十字型的徽標(並非紅十字會啊),如下所示(可參見p1.jpg)

                     $$$$$$$$$$$$$
                     $           $
                   $$$ $$$$$$$$$ $$$
                   $   $       $   $
                   $ $$$ $$$$$ $$$ $
                   $ $   $   $   $ $
                   $ $ $$$ $ $$$ $ $
                   $ $ $   $   $ $ $
                   $ $ $ $$$$$ $ $ $
                   $ $ $   $   $ $ $
                   $ $ $$$ $ $$$ $ $
                   $ $   $   $   $ $
                   $ $$$ $$$$$ $$$ $
                   $   $       $   $
                   $$$ $$$$$$$$$ $$$
                     $           $
                     $$$$$$$$$$$$$


    對方同時也需要在電腦dos視窗中以字元的形式輸出該標誌,並能任意控制層數。
    
    為了能準確比對空白的數量,程式要求對行中的空白以句點(.)代替。

輸入格式:
一個正整數 n (n<30) 表示要求列印圖形的層數

輸出:
對應包圍層數的該標誌。

例如:
使用者輸入:
1
程式應該輸出:
..$$$$$..
..$...$..
$$$.$.$$$
$...$...$
$.$$$$$.$
$...$...$
$$$.$.$$$
..$...$..
..$$$$$..

再例如:
使用者輸入:
3
程式應該輸出:
..$$$$$$$$$$$$$..
..$...........$..
$$$.$$$$$$$$$.$$$
$...$.......$...$
$.$$$.$$$$$.$$$.$
$.$...$...$...$.$
$.$.$$$.$.$$$.$.$
$.$.$...$...$.$.$
$.$.$.$$$$$.$.$.$
$.$.$...$...$.$.$
$.$.$$$.$.$$$.$.$
$.$...$...$...$.$
$.$$$.$$$$$.$$$.$
$...$.......$...$
$$$.$$$$$$$$$.$$$
..$...........$..
..$$$$$$$$$$$$$..

請仔細觀察樣例,尤其要注意句點的數量和輸出位置。


資源約定:
峰值記憶體消耗(含虛擬機器) < 64M
CPU消耗  < 1000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。

  

說實話,看見這種列印的題目就不想做了

import java.util.Scanner;
//2013-C-C第八題一樣的
public class _08列印十字圖 {

  static int N, L = 0, R;
  static char[][] arr;

  static void deal(int n) {
    //n=3,總寬17,最大下標16
    int l,r;
    l= (N-n)*2;//最小下標
    r=l+9+4*(n-1)-1;//最大下標
    for (int i = l+2; i <= r-2; i++) {
      arr[l][i] = '$';
      arr[r][i] = '$';
    }

    arr[l+1][l+2] = '$';
    arr[l+1][r-2] = '$';
    arr[r-1][l+2] = '$';
    arr[r-1][r-2] = '$';

    for (int i = l; i < l+3; i++) {
      arr[l+2][i] = '$';
      arr[r - 2][i] = '$';

    }
    for (int i = r; i >r-3 ; i--) {
      arr[l+2][i] = '$';
      arr[r - 2][i] = '$';
    }

    for (int i = l+3; i <= r-3; i++) {
      arr[i][l] = '$';
      arr[i][r] = '$';
    }
  }

  static void printAll() {
    for (int i = 0; i <= R; i++) {
      for (int j = 0; j <= R; j++) {
        if (arr[i][j] != '$') arr[i][j] = '.';
        System.out.print(arr[i][j]);
      }
      System.out.println();
    }
  }

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    N = sc.nextInt();
    R = 9 + 4 * (N - 1) - 1;//最大下標
    arr = new char[R+1][R+1];
    for (int i = N; i >=1 ; i--) {
      deal(i);
    }
    int c_i=2*N+2;
    int c_j=2*N+2;
    arr[c_i][c_j]='$';
    for (int i = c_i-2; i < c_i+3; i++) {
      arr[c_i][i]='$';
    }
    for (int i = c_i-2; i < c_i+3; i++) {
      arr[i][c_j]='$';
    }
    printAll();
  }

}


9.標題:買不到的數目

    小明開了一家糖果店。他別出心裁:把水果糖包成4顆一包和7顆一包的兩種。糖果不能拆包賣。

    小朋友來買糖的時候,他就用這兩種包裝來組合。當然有些糖果數目是無法組合出來的,比如要買 10 顆糖。

    你可以用計算機測試一下,在這種包裝情況下,最大不能買到的數量是17。大於17的任何數字都可以用4和7組合出來。

    本題的要求就是在已知兩個包裝的數量時,求最大不能組合出的數字。

輸入:
兩個正整數,表示每種包裝中糖的顆數(都不多於1000)

要求輸出:
一個正整數,表示最大不能買到的糖數

不需要考慮無解的情況

例如:
使用者輸入:
4 7
程式應該輸出:
17

再例如:
使用者輸入:
3 5
程式應該輸出:
7

 
資源約定:
峰值記憶體消耗(含虛擬機器) < 64M
CPU消耗  < 3000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。

  這題有個數學公式秒殺 a*b-(a+b),正常人想不到可以通過暴力列舉撈點分,甚至AC

public class _09買不到的數目 {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int a = sc.nextInt();
    int b = sc.nextInt();
    // System.out.println(a*b-a-b);

    int max = a * b;
    Set<Integer> ss = new HashSet<Integer>();
    //從0開始列舉到max
    for (int x = 0; a * x < max; x++) {
      for (int y = 0; a * x + b * y < max; y++) {
        ss.add(a * x + b * y);//用不小於0的x和y與係數能組合出來的數加入set中
      }
    }
    for (int i = max - 1; i >= 0; i--) {
      if (!ss.contains(i)) { //查詢第一個不在set中的值
        System.out.println(i);
        break;
      }
    }
  }
}


10.標題:剪格子

    如圖p1.jpg所示,3 x 3 的格子中填寫了一些整數。

    我們沿著圖中的紅色線剪開,得到兩個部分,每個部分的數字和都是60。

    本題的要求就是請你程式設計判定:對給定的m x n 的格子中的整數,是否可以分割為兩個部分,使得這兩個區域的數字和相等。
    如果存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。   
    如果無法分割,則輸出 0

程式輸入輸出格式要求:
程式先讀入兩個整數 m n 用空格分割 (m,n<10)
表示表格的寬度和高度
接下來是n行,每行m個正整數,用空格分開。每個整數不大於10000
程式輸出:在所有解中,包含左上角的分割區可能包含的最小的格子數目。


例如:
使用者輸入:
3 3
10 1 52
20 30 1
1 2 3

則程式輸出:
3

再例如:
使用者輸入:
4 3
1 1 1 1
1 30 80 2
1 1 1 100

則程式輸出:
10

(參見p2.jpg)


資源約定:
峰值記憶體消耗(含虛擬機器) < 64M
CPU消耗  < 5000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。

左邊圖1,右邊圖2

  

思路和普通的dfs搜尋+回溯一樣,只是這裡有個坑,可能會剪成3塊,搜出答案的時候,先把和第一個連線的答案存起來,然後用一次dfs求聯通塊判斷,最後別忘了 把求聯通塊時標記的vis復原,所以考慮到這個就不用boolean陣列標記了,改成int,靈活 

import java.util.Scanner;

public class _09剪格子 {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		m = in.nextInt();
		n = in.nextInt();
		a = new int[n][m];
		vis = new int[n][m];
		
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++) {
				a[i][j] = in.nextInt();
				sum += a[i][j];
			}
		if(sum%2==1) {
			System.out.println(0);
			return;
		}
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++) {
				vis[i][j] = 1;
				dfs(i,j,a[i][j],1);
				init();
			}
		if(ans==999999)
			System.out.println(0);
		else
			System.out.println(ans);
	}
	
	static int n,m,sum=0,ans=999999,q=0;
	static int[][] a;
	static int[][] vis; 
	static int[] dx = new int[] {0,1,0,-1};
	static int[] dy = new int[] {1,0,-1,0};
	
	
	static void dfs(int x,int y,int now,int t) {
		if(now==sum/2) {
			int tmp;
			if(vis[0][0]==1)
				tmp = t;
			else
				tmp = m*n-t;
//			for(int i=0;i<n;i++) {
//				for(int j=0;j<m;j++) 
//					System.out.print(vis[i][j]+" ");
//				System.out.println();
//			}
//			System.out.println();
			for(int i=0;i<n;i++)
				for(int j=0;j<m;j++)//判斷有沒剪成3部分了= =
					if(vis[i][j]!=1) {
						q=0;
						dfs_check(i, j);
						for(int ii=0;ii<n;ii++) 
							for(int jj=0;jj<m;jj++)
								if(vis[ii][jj]==-1)
									vis[ii][jj] = 0;
						if(q!=m*n-t)
							return;
						break;//判斷一次就行了
					}
			ans = Math.min(ans, tmp);
			return;
		}
		if(now>sum/2 || t>ans)//稍微剪剪枝
			return;
		for(int i=0;i<4;i++) {
			int j = x + dx[i];
			int k = y + dy[i];
			if(ok(j,k)) {
				vis[j][k] = 1;
				dfs(j,k,now+a[j][k],t+1);
				vis[j][k] = 0;
			}
		}
	}
	
	static void init() {
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				vis[i][j] = 0;
	}
	
	static boolean ok(int x,int y) {
		return x>=0 && x<n && y>=0 && y<m && vis[x][y]!=1;
	}
	
	static void dfs_check(int x,int y) {
		q++;
		vis[x][y] = -1;

		for(int i=0;i<4;i++) {
			int j = x + dx[i];
			int k = y + dy[i];
			if(j>=0 && j<n && k>=0 && k<m && vis[j][k]==0) 
				dfs_check(j,k);
		}
	}
	
}

貼一貼官方的c++程式碼

//
//  Created by zhengwei .
//  Copyright © 2019 lanqiao. All rights reserved.
//
/*
 
 標題:剪格子
 
 如圖p1.jpg所示,3 x 3 的格子中填寫了一些整數。
 
 我們沿著圖中的紅色線剪開,得到兩個部分,每個部分的數字和都是60。
 
 本題的要求就是請你程式設計判定:對給定的m x n 的格子中的整數,是否可以分割為兩個部分,使得這兩個區域的數字和相等。
 如果存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。
 如果無法分割,則輸出 0
 
 程式輸入輸出格式要求:
 程式先讀入兩個整數 m n 用空格分割 (m,n<10)
 表示表格的寬度和高度
 接下來是n行,每行m個正整數,用空格分開。每個整數不大於10000
 程式輸出:在所有解中,包含左上角的分割區可能包含的最小的格子數目。
 
 
 例如:
 使用者輸入:
3 3
10 1 52
20 30 1
1 2 3
 
則程式輸出:
3
 
 再例如:
 使用者輸入:
4 3
1 1 1 1
1 30 80 2
1 1 1 100
 
 則程式輸出:
 10
 
 (參見p2.jpg)
 
 
 資源約定:
 峰值記憶體消耗 < 64M
 CPU消耗  < 5000ms
 
 
 請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入...” 的多餘內容。
 
 所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
 
 注意: main函式需要返回0
 注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
 注意: 所有依賴的函式必須明確地在原始檔中 #include <xxx>, 不能通過工程設定而省略常用標頭檔案。
 
 提交時,注意選擇所期望的編譯器型別。
 
 */
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#define mk(i,j) make_pair(i,j)
using namespace std;

int m, n;
int total;
int g[10][10];
int ans;
/*抽象了一種剪輯方法*/
class Cut {
public:
    set<pair<int, int> > grids;//包含若干格子
    int sum;//所有格子的數值的求和
};
/**
 * 將st中的元素拷貝到新set中
 * @param st
 * @return
 */
set<pair<int, int> > copySet(set<pair<int, int> > &st) {
    set<pair<int, int> >::iterator iter = st.begin();
    set<pair<int, int> > ans;
    while (iter != st.end()) {
//        重新mkpair,加入新set中
        ans.insert(*iter);
        iter++;
    }
    return ans;
}
void add(int sum, int i, int j, set<pair<int, int> > &grids, Cut *&cut_new) {
    const pair<int, int> &pair_n = make_pair(i , j);
//                    深度拷貝set
    set<pair<int, int> > grids_copy=copySet(grids);
    grids_copy.insert(pair_n);
    cut_new->grids=grids_copy;
    cut_new->sum=sum+g[i][j];
}
vector<Cut *> vs[100];//分別儲存格子數為1~100的各種剪法

int main(int argc, const char *argv[]) {
    scanf("%d %d", &m, &n);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            scanf("%d", &g[i][j]);
            total += g[i][j];
        }
    }
//    第一個格子就是一半
    if (g[0][0] == total / 2) {
        printf("%d\n", 1);
        return 0;
    }

    /*左上角格子,只有一種剪法,加入v[1]*/
    Cut *c = new Cut();
    const pair<int, int> p = make_pair(0, 0);
    c->grids.insert(p);
    c->sum = g[0][0];
    vs[1].push_back(c);//只包含一個格子且包含00的只有一種剪法

    for (int i = 2; i <= m * n; ++i) {
//        i是格子數,用vs[i-1]裡面的來生成
//迭代vs[i-1]裡面的所有剪法
        for (int j = 0; j < vs[i - 1].size(); ++j) {
//            pCut代表一種剪輯方法
            Cut *pCut = vs[i - 1][j];
//            這種剪輯方法裡面記錄了所有格子,這些格子每個都擴充套件一個,即形成個數+1的剪法
            set<pair<int, int> > &grids = pCut->grids;
            int sum = pCut->sum;
            set<pair<int, int> >::iterator iter = grids.begin();
//            迭代所有的格子,嘗試新增它的鄰居
            while (iter != grids.end()) {
                const pair<int, int> &p = *iter;//代表一個格子的座標
                int x=p.first;
                int y=p.second;

                if(x+1<n&&grids.find(mk(x+1,y))==grids.end()){//下方,能走通且下方格子不在當前集合中

                    Cut *cut_new=new Cut();//生成一個新的剪法
                    add(sum, x+1, y, grids, cut_new);//將原有的格子全部拷入,再增加當前試探的新的格子
                    if(cut_new->sum==total/2){
                        printf("%d\n", i);
                        return 0;
                    }else if(cut_new->sum<total/2)
                        vs[i].push_back(cut_new);
                }
                if(x-1>=0&&grids.find(mk(x-1,y))==grids.end()){//上方
                    Cut *cut_new=new Cut();
                    add(sum, x-1, y, grids, cut_new);
                    if(cut_new->sum==total/2){
                        printf("%d\n", i);
                        return 0;
                    }else if(cut_new->sum<total/2)
                    vs[i].push_back(cut_new);
                }
                if(y+1<m&&grids.find(mk(x,y+1))==grids.end()){//右方
                    Cut *cut_new=new Cut();
                    add(sum, x, y+1, grids, cut_new);
                    if(cut_new->sum==total/2){
                        printf("%d\n", i);
                        return 0;
                    } else if(cut_new->sum<total/2)
                    vs[i].push_back(cut_new);
                }
                if(y-1>=0&&grids.find(mk(x,y-1))==grids.end()){//左方
                    Cut *cut_new=new Cut();
                    add(sum, x, y-1, grids, cut_new);
                    if(cut_new->sum==total/2){
                        printf("%d\n", i);
                        return 0;
                    }else if(cut_new->sum<total/2)
                    vs[i].push_back(cut_new);
                }
                iter++;
            }
        }
    }
    printf("%d\n", 0);
    return 0;
}



 

小結 

01 猜年齡 簡單列舉
02 組素數 帶重複元素的全排列+素數判定,用到set去重
03 馬虎的算式 列舉5個變數,組合後,進行檢查
*04 第39級臺階 走樓梯的一個變形,記錄走的步數,本質是遞迴思維
05 有理數類 物件導向和分數加法(通分和約分)
*06 逆波蘭表示式 遞迴的整體思維
07 核桃的數量 簡單列舉
08 列印十字圖 先寫死,再變活
**09 買不到的數目  數學
**10 剪格子 深搜+回溯+剪枝

搜尋(列舉=迭代和遞迴,dfs(含回溯和剪枝)、bfs、二分查詢)
模擬

相關文章