解題想法
這題我是參考Naukri的code,將他的dfs函式解讀,用自己的話寫而已。
接下來的解法是我自己解讀的,我並不知道他是不是這樣想的。
以範例測資為例:
一開始先做到
5 10 15 20 next=50
加入30
5 10 15 20 30 next=80
這時候再加40就>100,然後40的下一個是50,符合第19行的條件,直接return。
加入40
5 10 15 20 40 next=90
這時會遇到和加入30一樣的狀況,一樣return。
加入50
5 10 15 20 50 next=100
因為=100,所以輸出5 10 15 20 50。
5 10 15 20都做完了,所以i++,20換30。
5 10 15 30 next=60
在5 10 15 30的情況中,搭配40會剛好=100,所以輸出5 10 15 30 40。
以此類推,比較深的都窮舉完後就會慢慢的遞回來,最後做到不能再做為止就全部輸出完了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<iostream> #include<algorithm> using namespace std;
int n,m,i,l[35],rec[35]; char noAns=1;
void dfs(int crt,int start,int depth){ int i,j,next; for(i=start;i<n;i++){ next=crt+l[i]; rec[depth]=l[i]; if(next==m){ for(j=0;j<depth;j++){cout<<rec[j]<<" ";} cout<<l[i]<<endl; noAns=0; } if(next>m){ if(l[i]<l[i+1]){return;} } if(next+l[i]<=m){ dfs(next,i+1,depth+1); } } }
int main(){ cin>>n>>m; for(i=0;i<n;i++){cin>>l[i];} sort(l,l+n); dfs(0,0,0); if(noAns){cout<<"-1\n";} return 0; }
|
解題想法
感覺跟窮舉有關的題目都能用DFS來解,這題和a981 求和問題比較單純些,只須注意左括弧的數量是否大於等於右括弧的數量即可。
心得
這題我有用C和Cpp實作,也藉由這題讓我感受到ZeroJudge好像對C特別友善,畢竟之前寫UVA的題目,它是對Cpp使用者比較友善。
以下是我用C和Cpp測試的結果:
C
Cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include<stdio.h> #include<stdlib.h> #include<string.h>
char s[30],first=1; int n;
void dfs(int crt,int l,int r){ if(crt==2*n){ printf("%s\n",s); return; } if(l<n){ s[crt]='('; dfs(crt+1,l+1,r); } if(r<l){ s[crt]=')'; dfs(crt+1,l,r+1); } }
int main(){ while(scanf("%d",&n)==1){ memset(s,0,sizeof(s)); if(first==1){first=0;} else{printf("\n");} dfs(0,0,0); } return 0; }
|
這題是窮舉法,也是DFS裡很經典的題型。這題看解題報告好像有些人是用prev_permutation()這個函式,我待會也來試試看,不過我DFS的概念還不是很熟悉,所以還是想練一下。這次參考了Naukri的code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include<iostream> using namespace std;
int n, used[9]={0}, pw[8]={0};
void dfs(int len,int num){ int i;
if(len==n){ for(i=0;i<len;i++){cout<<pw[i];} cout<<endl; }
for(i=n;i>0;i--){ if(!used[i]){ used[i]=1;pw[len]=i; dfs(len+1, i-1); used[i]=0;pw[len]=0; } } }
int main(){ while(cin>>n){ dfs(0,n); } return 0; }
|
查了一下prev_permutation()怎麼用的時候,發現還有一個next_permutation(),兩者差在prev會將數字從字典序最大開始越排越小,而next會將數字從字典序最小開始越排越大。要使用這兩個函式需要先用algorithm這個函式庫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<iostream> #include<algorithm> using namespace std;
int main(){ int arr[8], i, n; while(cin>>n){ for(i=0;i<n;i++){arr[i]=n-i;} do{ for(i=0;i<n;i++){cout<<arr[i];} cout<<endl; } while(prev_permutation(arr, arr+n)); } return 0; }
|
這一題我參考了高中生解題系統-參考答案 zerojudge code的做法,先對陸地做標記,第一塊標記2、第二塊標記3,以此類推。然後再跑迴圈算出每塊地的東西南北、面積並印出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| #include<stdio.h> #define lim 514
int map[lim][lim];
void paint(int m,int y,int x){ map[y][x]=m;
if(map[y][x+1]!=1 && map[y-1][x]!=1 && map[y][x-1]!=1 && map[y+1][x]!=1){return;}
if(map[y][x+1]==1){ paint(m,y,x+1); }
if(map[y-1][x]==1){ paint(m,y-1,x); }
if(map[y][x-1]==1){ paint(m,y,x-1); }
if(map[y+1][x]==1){ paint(m,y+1,x); } }
int main(){ int x,y,i,j,mark=2; for(i=0;i<lim;i++){ for(j=0;j<lim;j++){ map[i][j]=0; } }
scanf("%d%d",&x,&y); for(i=1;i<=y;i++){ for(j=1;j<=x;j++){ scanf("%d",&map[i][j]); } }
for(i=1;i<=y;i++){ for(j=1;j<=x;j++){ if(map[i][j]==1){ paint(mark,i,j); mark++; } } }
int check=2; while(check<mark){ int area=0,w,n,e,s;
for(i=1;i<=y;i++){ for(j=1;j<=x;j++){ if(map[i][j]==check){ area++;
if(area==1){ w=j;e=j;n=i;s=i; } if(j<w){w=j;}
if(j>e){e=j;}
if(i>s){s=i;} } } }
printf("%d %d %d %d %d\n",w-1,n-1,e-1,s-1,area); check++; }
return 0; }
|
本來以為自己突發奇想的從後往前找會很快,結果…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include<stdio.h>
int main(){ int n,k,i; while(scanf("%d%d",&n,&k) != EOF){ int a[k],b[k];
for(i=0;i<k;i++){ scanf("%d%d",&a[i],&b[i]); }
int loc; for(i=0;i<k;i++){ if(b[i] == n){ loc = i; break; } }
int stair,find; while(a[loc]!=0){ stair = a[loc]; find = 0; for(i=0;i<k;i++){ if(b[i] == stair){ loc = i; find = 1; break; } } if(!find){break;} }
if(find){ printf("Ok!\n"); } else{ printf("Impossib1e!\n"); } } return 0; }
|
看來用DFS是無法避免的,函式的設計我參考了alan23273850 的刷題筆記,用這個函式跑會將所有點都跑完,最後依據res判斷是否能到達,第19行我也試過=,不過因為所有點都跑完 的特性,如果搜尋的最後一個點不是目的地,那麼dfs(0)就會是false,就算中途有遇到目的地也一樣。所以用|=,這樣只要中途有遇到目的地,true就不會被覆蓋掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<bits/stdc++.h> #define speedup ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) using namespace std;
int n; bool v[101]; vector<int> p[101];
bool dfs(int s){ if(s == n){return true;}
if(v[s]){return false;}
v[s] = true; bool res = false; for(int h=0 ; h<p[s].size() ; h++){ res |= dfs(p[s][h]); } return res; }
int main(){ speedup; int a,b,k,i; while(cin >> n >> k){ memset(v,false,sizeof(v)); for(i=0;i<101;i++){ p[i].clear(); } while(k--){ cin >> a >> b; p[a].push_back(b); } if(dfs(0)){ cout << "Ok!\n"; } else{ cout << "Impossib1e!\n"; } } return 0; }
|
封面圖源:Pixiv