蓝桥备赛第一周2021.1.11 递归 枚举 位运算
文章目錄
- 遞歸實現指數型枚舉
- 遞歸指數型枚舉
- 方法1:肯定是2^n行,所以直接就是上一個動態m從0到n加一堆空行
- 方法2:以最新的值為n為結束,遇到為0的不輸出,用完要恢復為0
- 遞歸實現排列型枚舉
- 非遞歸組合枚舉
- 這個用了很多東西,遞歸+棧轉棧+棧,state存狀態等等
- sumdiv
- 大佬解法,很清晰,我也喜歡,但有10%數據過不去
- 費解的開關
- 1.上面的位運算
- 2.memcpy
- 3.memcpy的兩個數組類型不能串!!!!!!,我人都傻了,把k給cpy了也不報個錯
- 4.還有bfs解法等我緩緩再弄吧
- 翻硬幣
- 思路:
- 思路
- 模擬下:(沒辦法啊,肯定都得翻,貪心咯)
- Strange Towers of Hanoi
- 法一:遞推式
- dp:
- 帶分數
- 第一個想法
- 大佬的dfs 46ms代碼
- Fractal Streets
- ~~又是自閉的分型,上次已經留下了深深的心理陰影~~
- 大意:
- << (n-1)其實就是乘2的n-1次方
- 另外兩個博客里面對第四塊坐標轉換一個+1一個-1是因為一個從0開始一個從1開始
遞歸實現指數型枚舉
鏈接:https://ac.nowcoder.com/acm/problem/50918
來源:牛客網
題目描述
從 1~n 這 n 個整數中隨機選出 m 個,輸出所有可能的選擇方案。n \gt 0n>0, 0 \leq m \leq n0≤m≤n, n+(n-m)\leq 25n+(n?m)≤25。
輸入描述:
兩個整數n,m。
輸出描述:
按照從小到大的順序輸出所有方案,每行1個。
首先,同一行內的數升序排列,相鄰兩個數用一個空格隔開。其次,對于兩個不同的行,對應下標的數一一比較,字典序較小的排在前面(例如1 3 9 12排在1 3 10 11前面)。
示例1
輸入
復制
5 3
輸出
復制
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
遞歸指數型枚舉
鏈接:https://ac.nowcoder.com/acm/problem/50911
來源:牛客網
題目描述
從 1\sim n1~n這 n (n \leq 16)(n≤16) 個整數中隨機選取任意多個,輸出所有可能的選擇方案。
輸入描述:
一個整數n。
輸出描述:
每行一種方案。同一行內的數必須升序排列,相鄰兩個數用恰好1個空格隔開。對于沒有選任何數的方案,輸出空行。本題有自定義校驗器(SPJ),各行(不同方案)之間的順序任意。
示例1
輸入
復制
3
輸出
復制
3
2
2 3
1
1 3
1 2
1 2 3
方法1:肯定是2^n行,所以直接就是上一個動態m從0到n加一堆空行
#include <iostream> #include<bits/stdc++.h> using namespace std; #define maxn 1005 ///回溯典型 int n,m; int feikong=0; int mem[maxn]={0}; void traceback(int val,int pos)///val是當前的選擇(已經選了(相當于棧頂)),///pos是當前位置(還沒有選) {if(pos==m+1){feikong++;for(int i=1;i<=m;i++){cout<<mem[i]<<" ";}cout<<endl;}///我喜歡在選擇的時候查約束for(int i=val+1;i<=n;i++){if((pos+n-i)<m)///比如說一共取四個數,第三個數那就不能為n{continue;}mem[pos]=i;traceback(i,pos+1);///不用撤回,直接覆蓋了}} int ercifnag(int q) {int res=1;for(int i=0;i<q;i++){res*=2;}return res; } int main() {//cout<<ercifnag(2);cin>>n;///?那就循環m唄for(m=1;m<=n;m++){//cout<<m;traceback(0,1);}for(int i=0;i<ercifnag(n)-feikong;i++){cout<<endl;}return 0; }方法2:以最新的值為n為結束,遇到為0的不輸出,用完要恢復為0
#include <iostream> #include<bits/stdc++.h> using namespace std; #define maxn 1005 ///回溯典型 int n,m; //int feikong=0; int mem[maxn]={0}; void traceback(int val,int pos)///val是當前的選擇(已經選了(相當于棧頂)),///pos是當前位置(還沒有選) {for(int i=1;i<=n;i++){if(mem[i])///恢復狀態使有的可以為0cout<<mem[i]<<" ";}cout<<endl;if(val==n){return ;//feikong++;}///我喜歡在選擇的時候查約束for(int i=val+1;i<=n;i++){if((pos+n-i)<m)///比如說一共取四個數,第三個數那就不能為n{continue;}mem[pos]=i;traceback(i,pos+1);mem[pos]=0;}} int ercifnag(int q) {int res=1;for(int i=0;i<q;i++){res*=2;}return res; } int main() {cin>>n;traceback(0,1);return 0; }遞歸實現排列型枚舉
鏈接:https://ac.nowcoder.com/acm/problem/50919
來源:牛客網
題目描述
把 1\sim n1~n 這 n(n \lt 10)(n<10)個整數排成一行后隨機打亂順序,輸出所有可能的次序。
輸入描述:
一個整數n。
輸出描述:
按照從小到大的順序輸出所有方案,每行1個。 首先,同一行相鄰兩個數用一個空格隔開。其次,對于兩個不同的行,對應下標的數一一比較,字典序較小的排在前面。
示例1
輸入
復制
3
輸出
復制
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
這個思路應該是交換,先寫下
懶得交換了,還是用記憶數組順手,,,
非遞歸組合枚舉
鏈接:https://ac.nowcoder.com/acm/problem/50925
來源:牛客網
從 1~n 這 n 個整數中隨機選出 m 個,輸出所有可能的選擇方案。n \gt 0n>0, 0 \leq m \leq n0≤m≤n, n+(n-m)\leq 25n+(n?m)≤25。
輸入描述:
兩個整數n,m。
輸出描述:
按照從小到大的順序輸出所有方案,每行1個。
首先,同一行內的數升序排列,相鄰兩個數用一個空格隔開。其次,對于兩個不同的行,對應下標的數一一比較,字典序較小的排在前面(例如1 3 9 12排在1 3 10 11前面)。
示例1
輸入
復制
5 3
輸出
復制
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
這個用了很多東西,遞歸+棧轉棧+棧,state存狀態等等
引下大佬的state解釋
sumdiv
鏈接:https://ac.nowcoder.com/acm/problem/50922
來源:牛客網
題目描述
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^BA
B
. Determine S modulo 9901 (the rest of the division of S by 9901).
輸入描述:
The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.
輸出描述:
The only line of the output will contain S modulo 9901.
示例1
輸入
復制
2 3
輸出
復制
15
說明
2^3 = 8.2
3
=8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15.
15 modulo 9901 is 15 (that should be output).。
2的3次方的所有因子求和
類似快速冪(用了快速冪,總之是種二分)
大佬解法,很清晰,我也喜歡,但有10%數據過不去
https://blog.csdn.net/yeguxin/article/details/47017955
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h>using namespace std;const int h = 10001; int n,m; struct node {int x;int y; }q[h];long long int power(long long int pn,long long int pm) ///反復平方法來求A^B 省時間 {long long int sq = 1;while(pm>0){if(pm%2){sq = (sq*pn)%9901;}pm = pm / 2;pn = pn * pn % 9901;}return sq; }long long int updata(long long int pn,long long int pm) ///遞歸二分求等比數列的和 ///對每一個質因子求1+p1+p1^2+p1^3+...p1^k1,正好用等比數列,等比數列就可以遞歸 {if(pm == 0){return 1;}if(pm%2){return (updata(pn,pm/2)*(1+power(pn,pm/2+1)))%9901; /// 當pm為奇數時,有公式來求等比數列的和 (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1)) }else{return (updata(pn,pm/2-1)*(1+power(pn,pm/2+1)) + power(pn,pm/2))%9901; ///當pm為偶數時,有公式來求等比數列的和 (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2); } }int main() {while(scanf("%d%d",&n,&m)!=EOF){int k = 0;for(int i=2;i*i<=n;) ///尋找質因子,一個很好的方法{if(n%i == 0){q[k].x = i;///q[k].x q[k].y是質因子和次數q[k].y = 0;while(n%i == 0){q[k].y++;n /= i;}k++;}if(i == 2){i++;///2和3是要算的}else{i = i + 2;///這樣就不用算偶數了}}if(n!=1)///最后一個是本身{q[k].x = n;q[k].y = 1;k++;}int ans = 1;for(int i=0;i<k;i++){ ans = (ans*(updata(q[i].x,q[i].y*m)%9901)%9901);///既然是n的m次方,那么就直接用n的質因子m次方就好,///這里相乘是約數和公式,對每一個因子求(1+p1+p1^2+p1^3+...p1^k1) ,///然后相乘}printf("%d\n",ans);}return 0; }費解的開關
鏈接:https://ac.nowcoder.com/acm/problem/50920
來源:牛客網
題目描述
你玩過“拉燈”游戲嗎?25盞燈排成一個5x5的方形。每一個燈都有一個開關,游戲者可以改變它的狀態。每一步,游戲者可以改變某一個燈的狀態。游戲者改變一個燈的狀態會產生連鎖反應:和這個燈上下左右相鄰的燈也要相應地改變其狀態。
我們用數字“1”表示一盞開著的燈,用數字“0”表示關著的燈。下面這種狀態
10111
01101
10111
10000
11011
在改變了最左上角的燈的狀態后將變成:
01111
11101
10111
10000
11011
再改變它正中間的燈后狀態將變成:
01111
11001
11001
10100
11011
給定一些游戲的初始狀態,編寫程序判斷游戲者是否可能在6步以內使所有的燈都變亮。
輸入描述:
第一行有一個正整數n,代表數據中共有n個待解決的游戲初始狀態。
以下若干行數據分為n組,每組數據有5行,每行5個字符。每組數據描述了一個游戲的初始狀態。各組數據間用一個空行分隔。
對于30%的數據,n \leq 5n≤5;
對于100%的數據,n \leq 500n≤500。
輸出描述:
輸出數據一共有n行,每行有一個小于等于6的整數,它表示對于輸入數據中對應的游戲狀態最少需要幾步才能使所有燈變亮。
對于某一個游戲初始狀態,若6步以內無法使所有燈變亮,請輸出“-1”。
示例1
輸入
復制
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
輸出
復制
3
2
-1
https://blog.csdn.net/weixin_42176113/article/details/107271626
這個里面的位運算寫法就是
用5位的k來存第一行的操作狀態
比如k=10010
而下面這行代碼就是遍歷k的每一位,看他是不是1
1.上面的位運算
2.memcpy
3.memcpy的兩個數組類型不能串!!!!!!,我人都傻了,把k給cpy了也不報個錯
4.還有bfs解法等我緩緩再弄吧
#include <iostream> #include<bits/stdc++.h> using namespace std; #define maxn 1005 const int INF=1e7+5; int dx[5]={0,-1,0,1,0}; int dy[5]={0,0,1,0,-1}; int myCheese[10][10];///這題一看暴力回溯必超時啊,像dp那么倒推?先試試魔方遍歷 //char backup[10][10];//!!!!!!!!!!!就tm這句改了一個點 int backup[10][10]; void init_cheese(){for(int i=0;i<6;i++){for(int j=0;j<6;j++){myCheese[i][j]=0;}} } void click(int x,int y){for(int i=0; i<5; i++){int tx=x+dx[i],ty=y+dy[i];if(tx>=0 && tx<5 && ty>=0 && ty<5){myCheese[tx][ty]+=1;myCheese[tx][ty]%=2;}}} //intint mofang() {int ans=INF;for(int k=0;k<(1<<5);k++){int cnt=0;//cout<<sizeof(myCheese)<<endl;//cout<<sizeof(int)*36<<endl;//cout<<k<<endl;memcpy(backup,myCheese,sizeof(myCheese));///第一行32種點擊可能,由本次嘗試的k決定for(int i=0;i<5;i++){//cout<<"old k"<<k<<endl;if(k>>i&1){click(0,i);cnt++;}// cout<<"new k"<<k<<endl;}///從0號行到3號行,有0的就點下一行for(int i=0;i<4;i++){for(int j=0;j<5;j++){if(myCheese[i][j]==0){click(i+1,j);cnt++;}}}///檢查最后一行int flag=1;for(int i=0;i<5;i++){if(myCheese[4][i]==0){flag=0;break;}}if(flag==1){ans=min(ans,cnt);}memcpy(myCheese,backup,sizeof(myCheese));}if(ans>6){ans=-1;}return ans;} int main() {int N;cin>>N;string buf;while(N--){for(int i=0;i<5;i++){cin>>buf;//cout<<"buf:"<<buf<<endl;for(int j=0;j<5;j++){myCheese[i][j]=buf[j]-'0';// myCheese[i][j]=(buf>>(5-1-j))&1;}}// for(int i=0;i<5;i++) // { // // for(int j=0;j<5;j++) // { // cout<<myCheese[i][j]<<" "; // } // cout<<endl; // }cout<<mofang()<<endl;;}return 0; }翻硬幣
鏈接:https://ac.nowcoder.com/acm/problem/14355?&headNav=acm
來源:牛客網
題目描述
小明正在玩一個“翻硬幣”的游戲。
桌上放著排成一排的若干硬幣。我們用 * 表示正面,用 o 表示反面(是小寫字母,不是零)。
比如,可能情形是:oo*oooo
如果同時翻轉左邊的兩個硬幣,則變為:oooo***oooo
現在小明的問題是:如果已知了初始狀態和要達到的目標狀態,每次只能同時翻轉相鄰的兩個硬幣,那么對特定的局面,最少要翻動多少次呢?
我們約定:把翻動相鄰的兩個硬幣叫做一步操作,那么要求:
輸入描述:
兩行等長的字符串,分別表示初始狀態和要達到的目標狀態。每行的長度<1000
輸出描述:
一個整數,表示最小操作步數。
示例1
輸入
復制
oo
輸出
復制
5
示例2
輸入
復制
ooo***
ooo***
輸出
復制
1
藍橋原題,似乎是個智力題?
超級進階:
https://www.cnblogs.com/kuangbin/p/3218060.html
思路:
1.模擬翻硬幣的過程
2.記錄不相同的位置,規律:相鄰兩個不相同硬幣的位置差即為這兩個之間需要翻轉的次數
思路
/思路:每一次不一樣肯定要翻兩枚枚硬幣,直到都一樣為止
模擬下:(沒辦法啊,肯定都得翻,貪心咯)
#include<stdio.h> #include<string.h> char String1[2000]; char String2[2000]; int main() {int i,n,sum;scanf("%s %s",String1,String2);n=strlen(String1);sum=0;for(i=0;i<n;i++){if(String1[i]!=String2[i]){if(String2[i+1]=='*'){String2[i+1]='o';}else{String2[i+1]='*'; }sum++;}}printf("%d\n",sum);return 0; }Strange Towers of Hanoi
鏈接:https://ac.nowcoder.com/acm/problem/50921
來源:牛客網
盤子個數取1-12的四漢諾塔
先用四塔算法,取固定1-n個(k)個,將剩下的用四塔算法從A-》B,然后,用三塔算法,從A-》D,然后用四塔算法從B-》D
但,最快的解法是用遞推式:
法一:遞推式
https://blog.csdn.net/yanglei253/article/details/93310024
#include <iostream> #include <cstring> using namespace std;int main() {int three[13],four[13];// 最初初始化為 0x3f 在最大流中是有很大用處的,這里雖然用不到memset(four,0x3f,sizeof(four));three[0] = 0;three[1] = 1;for(int i = 2;i < 13;i++){three[i] = 2 * three[i - 1] + 1;}four[0] = 0;for(int i = 0;i < 13;i++){for(int j = 0;j < i;j++){four[i] = min(four[i],2 * four[j] + three[i - j]);}}for(int i = 1;i < 13;i++){cout << four[i] << endl;}return 0; }dp:
https://blog.csdn.net/SSL_ZYC/article/details/81611372
#include <cstdio> #include <iostream> #define Inf 1e9 using namespace std;int f[5][21];int main() {f[3][1]=f[4][1]=1;for (int i=2;i<=12;i++){f[3][i]=f[3][i-1]*2+1; //初始化三個柱子的情況f[4][i]=Inf;}printf("1\n"); //一個盤子for (int i=2;i<=12;i++){for (int j=1;j<=i;j++)f[4][i]=min(f[4][j]*2+f[3][i-j],f[4][i]);printf("%d\n",f[4][i]);}return 0; }帶分數
第一個想法
遍歷小于N的所有數
100:1+99 2+98 。。。。。14+86
然后把前面的數標記進mark,后面的數用dfs造分數檢查
其中加點剪枝
更好的思路:枚舉整數和分母,求分子
https://moomhxy.blog.csdn.net/article/details/88768920?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control
但這個代碼其實不是bfs,就是純暴力,大晚上寫不動了
我的200多ms憨憨代碼
大佬的dfs 46ms代碼
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=46455914
#include <bits/stdc++.h> using namespace std; const int N=20; int n; int res=0; bool st[N],mark[N]; bool check(int a,int c) {int b=n*c-a*c;if(!a||!b||!c) return false;memcpy(mark,st,sizeof(st));while(b){int x=b%10;b=b/10;if(!x||mark[x]) return false;mark[x]=true;}for(int i=1;i<=9;i++){if(!mark[i]) return false;}return true; } void dfs_c(int u,int a,int c) {if(u>9) return;if(check(a,c)) res++;for(int i=1;i<=9;i++){if(!st[i]){st[i]=true;dfs_c(u+1,a,c*10+i);st[i]=false;}} } void dfs_a(int u,int a) //u表示當前用到了幾個數,a表示當前枚舉到的a的值 {if(a>=n) return;if(a) dfs_c(u,a,0); //帶分數中三個數都不能是0,在這里剪枝for(int i=1;i<=9;i++){if(!st[i]){st[i]=true;dfs_a(u+1,a*10+i);st[i]=false;}} } int main() {cin>>n;dfs_a(0,0);cout<<n<<" ";cout<<res<<endl;return 0; }Fractal Streets
又是自閉的分型,上次已經留下了深深的心理陰影
大意:
https://blog.csdn.net/xuechen_gemgirl/article/details/87975396
題面大概意思是:
給你一個原始的分形圖,t組數據,對于每組數據,輸入3個數n,h,o (n為在第n級,h,o為兩個房子的編號),求在第n級情況下,編號為h和o的兩個點之間的距離*10為多少。
其中,第n級分形圖形成規則如下:
首先先在右下角和右上角復制一遍n-1情況下的分形圖
然后將n-1情況下的分形圖順時針旋轉90度,放到左上角
最后將n-1情況下的分形圖逆時針旋轉90度 ,放到左下角
編號是從左上角那個點開始計1,沿著道路計數。
還有這說法的嗎,我大意了啊,沒有閃(另外第二張圖右上角標號錯了)
<< (n-1)其實就是乘2的n-1次方
https://blog.csdn.net/ben_xsy/article/details/79288058
另外兩個博客里面對第四塊坐標轉換一個+1一個-1是因為一個從0開始一個從1開始
#include <iostream> #include<bits/stdc++.h> using namespace std; #define maxn 1005 #define ll long longll p[40];///打打表,每一層最大值,別重復算了 void rec(ll n,ll s,ll &x,ll &y) {if(n==1)///翻到最底層了{if(s==1){x=1;y=1;}else if(s==2){x=1;y=2;}else if(s==4)///小地方不要犯錯{x=2;y=1;}else {x=2;y=2;}return ;}if(s<=p[n-1])///左上角{rec(n-1,s,x,y);///左上角要順時針90dell t;t=x;x=y;y=t;}else if(s<=2*p[n-1])///右上角{rec(n-1,s-p[n-1],x,y);///是倒推y+=(1ll<<(n-1));///用下面的求出下一層的x與y后,本層要加上本層的偏移}else if(s<=3*p[n-1]){///右下角rec(n-1,s-2*p[n-1],x,y);x+=(1ll<<(n-1));y+=(1ll<<(n-1));}else{///左下rec(n-1,s-3*p[n-1],x,y);///x與y是什么受下層影響,所以也可以不管,直接輸x,y,然后///下面兩個式子賦值的時候把x和y互換///(這個是優化后的了)ll xyuan,yyuan;xyuan=x;yyuan=y;x=(1ll<<n)+1-yyuan;y=(1ll<<(n-1))+1-xyuan;}}int main() {int t;cin>>t;p[1]=4;for(int i=2;i<=39;i++){p[i]=p[i-1]*4;// cout<<p[i]<<endl;}ll n,A,B,x1,y1,x2,y2;while(t--){cin>>n>>A>>B;rec(n,A,x1,y1);rec(n,B,x2,y2);printf("%.0lf\n",sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))*10);}return 0; }總結
以上是生活随笔為你收集整理的蓝桥备赛第一周2021.1.11 递归 枚举 位运算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在pytorch中自定义dataset读
- 下一篇: 线段树学习笔记