題目:
今天是陰歷七月初五,acm隊員zb的生日。zb正在和C小加、never在武漢集訓。他想給這兩位兄弟買點什么慶祝生日,經過調查,zb發現C小加和 never都很喜歡吃西瓜,而且一吃就是一堆的那種,zb立刻下定決心買了一堆西瓜。當他准備把西瓜送給C小加和never的時候,遇到了一個難 題,never和C小加不在一塊住,只能把西瓜分成兩堆給他們,為了對每個人都公平,他想讓兩堆的重量之差最小。每個西瓜的重量已知,你能幫幫他么?
輸入:
多組測試數據(<=1500)。數據以EOF結尾
第一行輸入西瓜數量N (1 ≤ N ≤ 20)
第二行有N個數,W1, …, Wn (1 ≤ Wi ≤ 10000)分別代表每個西瓜的重量
輸出:
輸出分成兩堆后的質量差
樣例輸入:
5
5 8 13 27 14
樣例輸出:
3
下面是源程序:
#include<stdio.h> #include<iostream> #include<cstdlib> #include<fstream> using namespace std; int melon[21]; int n; int mins;//不要定義為min或max,以免與庫函數沖突 int total; void findmin(int i,int sum)//i為層數;sum為當前和 { if(i==n) //到i = n;時,最后一次討論是否包含[n-1]; { int temp=abs(total - sum - sum);//當前兩部分的差額 if(mins > temp) mins=temp; return; } /* 這里是一個剪枝;不用所有的都走到底部。 if(sum > total/2 )//如果當前的和已經大於一半,剪枝 { int temp=abs(total - sum - sum);//當前兩部分的差額 if(mins > temp)//如果最小差額比當前的差額大 { mins = temp; } return ; } */ findmin(i+1,sum);//不算melon[i] findmin(i+1,sum+melon[i]);//算上melon[i] } int main() { int i; while(cin>>n) { total=0; mins=9999999; for(i=0;i<n;i++) { scanf("%d",&melon[i]); total+=melon[i];//西瓜總重量 }//for if(n==0) cout<<melon[0]<<endl; else { findmin(0,0); cout<<mins<<endl; } }//while return 0; }
深度優先,就是從一個點出發,一直深入下去,直到葉子節點,然后返回,在深入,直到
遍歷完所有的節點,為了深刻的理解遍歷的過程,可以單步跟蹤一下。
單步跟蹤:遇到函數按F11,進入函數;進入函數后按F10,單步走。這樣就可以清楚的查看每一步。
下面就是跟從得到的樹狀圖:
注釋:輸入的數據是: 2 1 2
就是從兩個,分別是1和2 [0]代表第0個,[1]代表第1個。
遍歷時就會把所有的情況都遍歷一遍,每個元素都可能在或不在集合中。
問題2 素數環
有一個整數n,把從1到n的數字無重復的排列成環,且使每相鄰兩個數(包括首尾)的和都為素數,稱為素數環。
為了簡便起見,我們規定每個素數環都從1開始。例如,下圖就是6的一個素數環。
輸入:
有多組測試數據,每組輸入一個n(0<n<20),n=0表示輸入結束
輸出:
每組第一行輸出對應的Case序號,從1開始。
如果存在滿足題意敘述的素數環,從小到大輸出。
否則輸出No Answer。
樣例輸入:
6
8
3
0
樣例輸出:
分析:
(1)如果n為奇數,那么必然有兩個奇數相鄰,他們的和為偶數,不是素數。
(2)一邊排列一邊檢查,提前剪枝。
(3)遞歸思想:如果要求一個數組,或者是否包含某些元素,都是在main{}中調用時
令層數t=0,傳遞,然后在遞歸函數中增加t,如f(t+1);這樣就深入了。
還要有判斷條件一般都是t==n時,判斷是不是滿足某些條件,然后再返回。
當然,剪枝就是提前返回,就是t沒有到n時就返回了。
下面是源代碼:
#include<iostream>
#include<stdio.h>
using namespace std;
//直接通過一個數對應的索引號查看是不是素數
bool IsPrime[]={
0, 0, 1, 1, 0, 1, 0,
1, 0, 0, 0, 1, 0, 1,
0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0,
0, 0, 1
};//因為n<20;最大素數和為19+18=37;
void Output(int a[],int n)
{
int i;
cout<<"1";
for(i = 1 ; i < n ; i++ )
{
cout << " "<<a[i];
}
cout<<endl;
}
bool IsOk(int a[],int lastindex,int cur)//判斷當前序列是不是合法
{
if(lastindex < 0) //!!!!!!!!!關鍵,0 號沒有前驅,千萬不要寫成lastindex < 1
return true;
if(!IsPrime[cur+a[lastindex]]) //加入時與前一個元素的和不是素數
return false;
for(int i = 0;i <= lastindex; ++i)//判斷這個數字是不是被用過了
{
if(cur == a[i])
return false;
}
return true;
}
void PrimeCircle(int a[],int n,int t)//判斷是不是素數環,t為層數
{
if(n == 1)
{
cout<<"1"<<endl;
return;
}
if((n & 1) ==1 )//奇數沒有素數環
return;
if(n == t)//結束條件一般都是t==n, 因為遞歸時用到t+1,傳到下層為n=t+1;
//所以t=[n-1]是末尾
{
if(IsPrime[a[0] + a[n-1]]) //判斷首尾
Output(a,n);
return;
}
for(int i = 1;i <= n; ++i) //判斷1 到 n 是否滿足條件
{
a[t] = i;//
if(IsOk(a, t-1, i)) //判斷新加入的值是不是滿足條件
PrimeCircle(a, n ,t+1);//深入
}
}
int main()
{
int n;
int cnt=0;
while(++cnt)
{
scanf("%d",&n);
if(n == 0)
{
break;
}
int ans[20]={1};
printf("Case %d:\n",cnt);
//因為要多次返回,所以不能這么判斷bool flag=PrimeCircle(ans,n,1);
//一般都是從t=0層開始,這里第一個數已經定為1
if( n % 2 == 0 || n ==1 )
PrimeCircle(ans,n,1);
else
printf("No Answer\n");
}
return 0;
}
注:http://www.cnblogs.com/graphics/archive/2010/03/27/1698403.html