洛谷题单1-7 搜索题解
P1219 [USACO1.5]八皇后 Checker Challenge
這個是我最最開始的寫法,也是篇幅最大的寫法,每放一個棋子就考慮放這個棋子滿不滿足 每行每列,左斜線和右斜線都只有一個棋子,注意左斜線和右斜線是有規律的,右斜線的格子橫縱坐標和都是一樣的,左斜線上的橫縱相減是一樣的,左斜對角線下的左斜線差小于0,因為數組不能有負數,全加一個他們差的絕對值得最大值(隨便一個更大得數也行,不能小),剩下的就是dfs爆搜了,沒什么講的 , 其實有個更簡單的寫法,但意思是一樣的,就不貼上來了
#include<bits/stdc++.h> using namespace std; int hang[15]={0},lie[15]={0},zuoxie[50]={0},youxie[50]={0},n,mark=1,count2=0,count1=0;; vector<int>s1; void find1(){count1++;if(count2<=2){for(int i=0;i<s1.size();i++){printf("%d ",s1[i]);}printf("\n");count2++;}return ; } void dfs(int n,int y){lie[y]=1;for(int i=1;i<=n;i++){if(hang[i]==0&&zuoxie[i-y+14]==0&&youxie[i+y]==0){hang[i]=1;zuoxie[i-y+14]=1;youxie[i+y]=1;s1.push_back(i);if(y!=n){dfs(n,y+1);}else find1();s1.pop_back();hang[i]=0;zuoxie[i-y+14]=0;youxie[i+y]=0; }} }int main(){cin>>n;dfs(n,1);printf("%d",count1);return 0; }P2392 kkksc03考前臨時抱佛腳
這個題就是很簡單的01背包問題,找一半的錢最多能買多少,再用總錢減去做和就可以了,直接貼代碼了
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a[25],dp[1500]; int main(){int s[5],ans=0,q,q2;scanf("%d %d %d %d",&s[1],&s[2],&s[3],&s[4]);for(int i=1;i<=4;i++){memset(dp,0,sizeof(dp));memset(a,0,sizeof(a));int sum=0;for(int j=0;j<s[i];j++){scanf("%d",&a[j]);sum+=a[j];}for(int j=0;j<s[i];j++){for(int k=sum/2;k>=a[j];k--){dp[k]=max(dp[k],dp[k-a[j]]+a[j]);}}ans+=sum-dp[sum/2];}printf("%d",ans);return 0; }P1443 馬的遍歷
這個題是一個基礎bfs遍歷題,馬只能走八個方向,直接遍歷一遍就可以了,記得剪枝
#include<bits/stdc++.h> using namespace std; struct shuang{int x,y;int step; }; int a[405][405],b[405][405]; int n,m,x1,y123; int main(){int sx[]={-2,-2,-1,-1,1,1,2,2},sy[]={-1,1,-2,2,-2,2,-1,1};shuang q1;queue<shuang>s1;scanf("%d %d %d %d",&n,&m,&x1,&y123);int count=1;memset(a,-1,sizeof(a));memset(b,0,sizeof(b));a[x1][y123]=0;b[x1][y123]=1;q1.x=x1,q1.y=y123;q1.step=0;s1.push(q1);while(!s1.empty()){shuang q2=s1.front();s1.pop();for(int i=0;i<8;i++){int xx=q2.x+sx[i];int yy=q2.y+sy[i];if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&b[xx][yy]==0){shuang q3;b[xx][yy]=1;q3.x=xx;q3.y=yy;q3.step=q2.step+1;a[xx][yy]=q3.step;s1.push(q3); // printf("xx=%d yy=%d step=%d\n",xx,yy,q3.step);}}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){printf("%-5d",a[i][j]);}printf("\n");}return 0; }P1135 奇怪的電梯
這個題應該是最經典的bfs了,用結構體記錄一下上電梯的次數就行
#include<bits/stdc++.h> using namespace std; struct cheng{int q;int step; }; int x,n,qi,zhong,mark=0,lc1,lc2,ans; int a[205],b[205]={0}; int bfs(cheng o){queue<cheng>s1;s1.push(o);while(!s1.empty()){cheng m1=s1.front();s1.pop();if(m1.q==zhong){return m1.step;}lc1=m1.q+a[m1.q];if(lc1<=n){if(b[lc1]==0){b[lc1]=1;cheng m2;m2.q=lc1;m2.step=m1.step+1;s1.push(m2); }}lc2=m1.q-a[m1.q];if(lc2>=1&&b[lc2]==0){b[lc2]=1;cheng m3;m3.q=lc2;m3.step=m1.step+1;s1.push(m3);} }return -1; } int main(){cheng o;scanf("%d %d %d",&n,&qi,&zhong);for(int i=1;i<=n;i++){scanf("%d",&x);a[i]=x;}b[qi]=1;o.q=qi,o.step=0;ans=bfs(o);printf("%d",ans);return 0; }P2895 [USACO08FEB]Meteor Shower S
這個題怎么說呢,我數組一開始卡死在了300,debug了好久,長記性了以后數組開大一些,別卡的那么極限,難點在一開始的數據處理吧,一個地方可能會被不同時間的流星撞,我們要保證記錄的是最早的被撞時間,當然,第0秒被撞的直接給他一個大值,后面就是bfs中加一個比對時間就行
#include<bits/stdc++.h> using namespace std; struct zou{int x,y;int time1; }; zou m1; int n,x1,y12,t,tmin=100005; int sx[]={-1,0,1,0},sy[]={0,1,0,-1}; int a[305][305],b[305][305]; void bfs(zou m){queue<zou>s1;s1.push(m);while(!s1.empty()){zou m3=s1.front();s1.pop();if(a[m3.x][m3.y]==0){tmin=m3.time1;return ;}for(int i=0;i<4;i++){zou q3;int xx=m3.x+sx[i];int yy=m3.y+sy[i];if(xx>=0&&xx<=302&&yy>=0&&yy<=302&&b[xx][yy]==0){if(a[xx][yy]==0||m3.time1+1<a[xx][yy]){b[xx][yy]=1;q3.x=xx;q3.y=yy;q3.time1=m3.time1+1;s1.push(q3);}}}} } int main(){memset(a,0,sizeof(a));memset(b,0,sizeof(b));b[0][0]=1;cin>>n;for(int i=0;i<n;i++){scanf("%d %d %d",&x1,&y12,&t);if(a[x1][y12]==0){a[x1][y12]=t;}if(a[x1][y12]>t){a[x1][y12]=t;}for(int j=0;j<4;j++){int q1=x1+sx[j];int q2=y12+sy[j];if(q1>=0&&q2>=0){if(a[q1][q2]==0){a[q1][q2]=t;if(t==0){a[q1][q2]=100000;}}else if(a[q1][q2]>t){a[q1][q2]=t;if(t==0){a[q1][q2]=100000;} }}}}m1.time1=0;m1.x=0;m1.y=0;bfs(m1); if(tmin==100005){printf("-1");}else printf("%d",tmin);return 0; }P1036 [NOIP2002 普及組] 選數
這個題目我也沒有想到什么特別好的方法,直接爆搜的
#include<bits/stdc++.h> using namespace std; int x[20],n,k; bool is(int n){int s=sqrt(double(n));for(int i=2;i<=s;i++){if(n%i==0)return false;}return true; } int rule(int m1,int sum1,int start,int end){//m1是還有幾個數每加,sum1為和,start是當前是第幾個數,end是一共有幾個數 if(m1==0)return is(sum1);int sum=0;for(int i=start;i<=end;i++){sum+=rule(m1-1,sum1+x[i],i+1,end);}return sum; } int main(){cin>>n>>k;for(int i =0;i<n;i++)cin>>x[i];cout<<rule(k,0,0,n-1);return 0; }P2036 [COCI2008-2009#2] PERKET
這個就是一個選與不選的問題,也是直接dfs爆搜就行,當然最開始的數據處理給他單個食材的最小差值比較好
#include<bits/stdc++.h> using namespace std; int n,minn=10000000; struct shicai{int suandu,kudu;int cha; }; shicai s1[11]; void dfs(int i,int suan,int ku){if(abs(suan-ku)<minn&&ku!=0){minn=abs(suan-ku);}if(i>=n){return ;} dfs(i+1,suan*s1[i].suandu,ku+s1[i].kudu);dfs(i+1,suan,ku);return ; } int main(){cin>>n;for(int i=0;i<n;i++){scanf("%d %d",&s1[i].suandu,&s1[i].kudu);s1[i].cha=abs(s1[i].suandu-s1[i].kudu);if(s1[i].cha<minn){minn=s1[i].cha;}}dfs(0,1,0);printf("%d",minn);return 0; }P1433 吃奶酪
說到這個題那我可就起勁了,首先我們要知道什么是狀壓思想,我們不是一共有n個奶酪嗎,我們假設一個數字,這個數字轉化成二進制有n位,我們令他對應的位為1時表示吃過了這個奶酪,那么我們每一個數字對應的二進制狀態就是已經吃了幾個奶酪,我們把每個狀態,即1到n個1(二進制)遍歷一遍,就是所有吃奶酪過程中可能出現的狀態,然后在每個狀態中把能更新的距離都更新一遍,就有狀態方程
F(i,k)=min ( F ( i , k ) , F ( i , k - (i-1<<1) ) +a[ i ] [ j ] )
即從 j 點到 i 點距離的更新,k是狀態,我們要保證k是經過 i 點的,而且沒到 i 點前沒經過 i 點,這聽起來很像廢話,但這就是細節,我們怎么保證呢?
( k-( 1<< ( i-1 ) ) )& ( 1<< ( i-1 ) ) = = 0,表示沒經過這個點,但是注意了,我們現在是更新后的點,即我們現在是k狀態,即我們要保證上面成立 ,要先保證 k & ( 1<< ( i-1 ) ) = = 1成立,其實就是你要先到達這個點了,才知道不經過這個點,從其他已經經過的點到達這能不能更新距離,建議配合代碼理解,死看是八行的
P1605 迷宮
這個題也不多說了,沒有新意的dfs爆搜
#include<cstdio> #include<queue> #include<cstring> int a[10][10],sx[]={-1,1,0,0},sy[]={0,0,-1,1},vis[10][10]; int n,m,t,ans=0,qix,qiy,zhongx,zhongy,t1,t2; void dfs(int x,int y){if(x==zhongx&&y==zhongy){ans++;return ;}for(int i=0;i<4;i++){int xx=x+sx[i];int yy=y+sy[i];if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&vis[xx][yy]==0&&a[xx][yy]==0){vis[xx][yy]=1;dfs(xx,yy);vis[xx][yy]=0;}} } int main(){memset(a,0,sizeof(a));memset(vis,0,sizeof(vis));scanf("%d %d %d",&n,&m,&t);scanf("%d %d %d %d",&qix,&qiy,&zhongx,&zhongy);for(int i=0;i<t;i++){scanf("%d %d",&t1,&t2);a[t1][t2]=1;}vis[qix][qiy]=1;dfs(qix,qiy);printf("%d",ans);return 0; }P1019 [NOIP2000 提高組] 單詞接龍
這個題我是卡在比較重疊字母那里了,后來得到大佬指點,我們可以從一個字母開始比較,直到比到最多的字母完全相同,返回這個數字,不斷遞歸更新一個最長長度與是否更新,直到把所有能接龍的都遍歷一遍就可以了
對了對了,主函數那個空格是為了防止是以單個字母來開始接龍(重疊函數要求最短長度也要為2)
#include<cstdio> #include<cstring> #include<iostream> int n,vis[25]={0},len=0; using namespace std; string str[25]; int chongdie(string str1,string str2){for(int i=1;i<min(str1.length(),str2.length());i++){//比幾個字母 int mark=1;for(int j=0;j<i;j++){if(str1[str1.length()-i+j]!=str2[j]){//這個位置的字母不重疊,沒辦法接龍 mark=0;break;}}if(mark){return i;}}return 0; } void search(string str1,int lens){int c;len=max(len,lens);for(int i=0;i<n;i++){if(vis[i]>=2){continue;} c=chongdie(str1,str[i]);if(c>0){vis[i]++;search(str[i],lens+str[i].length()-c);vis[i]--;} } } int main(){scanf("%d",&n);for(int i=0;i<=n;i++){cin>>str[i];}search(' '+str[n],1);printf("%d",len);return 0; }P1101 單詞方陣
我這里沒想到什么特別好的方法,就是找到有y的地方,往所有方向走六步,假如與izhong相等那就是對的,在b數組里賦值一下即可
#include<cstdio> #include<cstring> #include<queue> #include<iostream> using namespace std; char a[105][105],b[105][105]; int sx[]={-1,-1,-1,0,0,1,1,1},sy[]={-1,0,1,-1,1,-1,0,1},n,k=0; int x[10005],y[10005]; char s1[]={"gnohzi"}; int main(){cin>>n;for(int i=0;i<n;i++){for(int j=0;j<n;j++){b[i][j]='*';cin>>a[i][j];if(a[i][j]=='y'){x[k]=i;y[k++]=j;}}}while(k--){for(int i=0;i<8;i++){int mark=1;int xx=x[k]+(sx[i]*6);int yy=y[k]+(sy[i]*6);if(xx>=0&&xx<n&&yy>=0&&yy<n){for(int j=5;j>=0;j--){if(a[xx-(sx[i]*j)][yy-(sy[i]*j)]!=s1[j]){mark=0;}}if(mark){b[x[k]][y[k]]='y';for(int j=1;j<=6;j++){b[x[k]+(sx[i]*j)][y[k]+(sy[i]*j)]=s1[5-(j-1)];}}}}}for(int i=0;i<n;i++){for(int j=0;j<n;j++){printf("%c",b[i][j]);}printf("\n");}return 0; }P2404 自然數的拆分問題
這個題,需要一點細節,看了代碼就會寫
#include<cstdio> #include<cstring> int n; int a[10000]={1}; void shuchu(int t){for(int i=1;i<t;i++){printf("%d+",a[i]);}printf("%d\n",a[t]); } void find(int x,int t){for(int i=a[t-1];i<=x;i++){if(i<n){a[t]=i;x-=i;if(x==0){shuchu(t);}else find(x,t+1);x+=i;}} } int main(){scanf("%d",&n);find(n,1); return 0; }P1596 [USACO10OCT]Lake Counting S
這個題沒怎么細想,直接記錄一下所有有水的地方(直接爆搜怕TLE),然后對每個有水的地方開始bfs并標記,然后從下一個沒有被標記的有水的地方繼續bfs
#include<cstdio> #include<cstring> #include<queue> #include<iostream> #include<utility> using namespace std; int vis[105][105],x[11000],y[11000],k=0,ans=0,n,m; int sx[]={-1,-1,-1,0,0,1,1,1},sy[]={-1,0,1,-1,1,-1,0,1}; char a[105][105]; queue<pair<int,int> >s1; int main(){memset(vis,0,sizeof(vis));scanf("%d %d",&n,&m);for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>a[i][j];if(a[i][j]!='W'){vis[i][j]=1;}else {x[k]=i;y[k++]=j;}}}for(int i=0;i<k;i++){if(vis[x[i]][y[i]]){continue;}vis[x[i]][y[i]]=1;ans++;s1.push({x[i],y[i]});while(!s1.empty()){pair<int,int>t=s1.front();s1.pop();for(int j=0;j<8;j++){int xx=t.first+sx[j];int yy=t.second+sy[j];if(xx>=0&&xx<n&&yy>=0&&yy<m&&vis[xx][yy]==0){vis[xx][yy]=1;s1.push({xx,yy});}}}}printf("%d",ans);return 0; }P1162 填涂顏色
這個題其實我不算寫出來,我的思路有問題,但剛好測試數據沒出到,我算是卡著數據過的,因為它中間會用1卡出一個閉環,那么我們把外面所有的0都標記一下,最后遍歷一遍,沒有被標記的0就在閉環里面,變成2就好,但是如果直接就是最外面就是閉環或者直接(1,1)是1,我這個就不成立了
僅供參考吧
P1032 [NOIP2002 提高組] 字串變換
這個題用優先隊列和map加substr過的(substr函數用不用都行,能寫就行)
a.substr(3,5)表示a這個字符串的第三位開始的后五位 , 這題綜合性挺強的
P1825 [USACO11OPEN]Corn Maze S
這個題一開始到傳送站我直接讓他移動而且把兩邊都標記了,直接wa了,后來仔細想了一下,傳送未必是好事,而且也不一定只傳送一次,當然最多兩次(過去或者回來),那我們在遍歷的時候對有字母的特判一下就行,只標記傳送過來的傳送點(出發傳送點),然后把當前的坐標變為傳送后的點,這樣再上下左右移動還會有一次回去來判斷有沒有更近的,其他的就是簡單的迷宮了
#include<queue> #include<iostream> #include<string> #include<map> #include<cstring> #include<cstdio> #include<utility> using namespace std; int vis[500][500]; char a[500][500]; struct node{int x;int y;int step; }; struct cmp{bool operator()(node a,node b){return a.step>b.step;} }; int n,m,qix,qiy; int sx[]={-1,0,0,1},sy[]={0,-1,1,0}; node find(int x,int y,int st){node ans;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){// printf("%d %d\n",a[x][y],a[i][j]);if(a[i][j]==a[x][y]&&(i!=x||y!=j)){ans.x=i;ans.y=j;ans.step=st;return ans;}}} } void bfs(){node t2;priority_queue<node,vector<node>,cmp>q;q.push({qix,qiy,0});while(!q.empty()){node t=q.top();q.pop(); // printf("%d %d\n",t.x,t.y);if(a[t.x][t.y]=='='){printf("%d\n",t.step);return ;}if(vis[t.x][t.y]==1){continue;}vis[t.x][t.y]=1;if(a[t.x][t.y]>='A'&&a[t.x][t.y]<='Z'){t=find(t.x,t.y,t.step);}for(int i=0;i<4;i++){int xx=t.x+sx[i];int yy=t.y+sy[i];if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&vis[xx][yy]==0&&a[xx][yy]!='#'){// printf("jin l %d %d\n",xx,yy);q.push({xx,yy,t.step+1});}}} } int main(){cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];if(a[i][j]=='@'){qix=i;qiy=j;}}}bfs();return 0; }總的來說搜索這東西,暴力出奇跡,偏分過樣例 ,就吃一個熟練度,你多敲幾個題目就會越來越得心應手,還是要加油啊,米娜桑
總結
以上是生活随笔為你收集整理的洛谷题单1-7 搜索题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020 macbook pro 16寸
- 下一篇: WEB密码安全输入控件