kuangbin专题——简单搜索

A - 棋盘问题

 POJ - 1321 

题意  

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

解法:n皇后的变形,注意放的位置不一定,并不是每一行都要放,计个step,然后dfs每一个点时,记得回溯上去处理一下,把vis[i]置为0,step--即可,然后处理完此次结束后,dfs(x+1),处理下一位;

#include<cstdio>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
int step,ans,n,k; 
char mp[20][20];
bool vis[20];
void dfs(int x){
     if(step==k){
     ans++;
     return ;
     }
     if(x>=n)return ;
     for(int i=0;i<n;i++){
     if(!vis[i]&&mp[x][i]=='#'){
     step++;
     vis[i]=1;
     dfs(x+1);
     vis[i]=0;
     step--;
     }
     }
     dfs(x+1);
}
int main(){
    while(~scanf("%d %d",&n,&k)){
    memset(vis,0,sizeof vis);
    if(n+k<0)break;
    rep(i,0,n-1){
    scanf("%s",mp[i]);
    }
    ans=step=0;
    dfs(0);
    printf("%d\n",ans);
    
    }
    return 0;    
}
View Code

B - Dungeon Master

 POJ - 2251

题意:就是个三维迷宫,直接广搜即可,用一个方向数组

#include<bits/stdc++.h>
#include<queue>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
const int maxn=1e5+5;
bool isprime[maxn];
void makeprime(){
     memset(isprime,true,sizeof isprime);
     isprime[1]=0;
     for(int i=2;i*i<=maxn;i++){
     if(isprime[i]==0)continue;
     for(int j=i*2;j<=maxn;j+=i){
     isprime[j]=0;
     }
     }
//     rep(i,1,maxn){
//     cout<<i<<" "<<isprime[i]<<endl;
//     }
}
struct point{int num,step;};
int ans,p,q;
bool vis[maxn]; 
bool  bfs(){
      memset(vis,0,sizeof vis);
     vis[p]=1;
     point tmp,next;
     tmp.num=p,tmp.step=0;
     queue<point>Q;
     Q.push(tmp);
     while(!Q.empty()){
     tmp=Q.front();
     Q.pop();
     vis[tmp.num]=1;
     if(tmp.num==q){
     ans=tmp.step;
     return true;
     } 
     int t=tmp.num;
     for(int i=0;i<4;i++){
     for(int j=0;j<=9;j++){
     t=tmp.num;
     if(i==0){
     t-=t%10;
     t+=j;
     }
     else if(i==1){
     t-=t%100/10*10;
     t+=j*10;
     }
     else if(i==2){
     t-=t%1000/100*100;
     t+=j*100;
     }
     else if(i==3){
     t-=t%10000/1000*1000;
     t+=j*1000;
     }
     if(t==tmp.num||t<1000||vis[t]||!isprime[t])continue;
     next.num=t,next.step=tmp.step+1;
     vis[t]=1;
     Q.push(next);
     }
     }
     
     }
     return false;
}
int main(){
    makeprime();
    int cas;
    scanf("%d",&cas);
    while(cas--){
    memset(vis,0,sizeof vis);
    scanf("%d %d",&p,&q);
    ans=0;
    if(bfs())printf("%d\n",ans);
    else printf("Impossible\n"); 
    
    }
    return 0;
}
View Code

 

 题意:给你n和m,问几步能走到,直接广搜即可

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
#define pb push_back
#define fi first
#define se second
#define check(x) (x<=N&&x>=0)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
const int N=1e5+5;
bool vis[N];
int step[N];
int n,k;
void bfs(){
     queue<int>Q;
     vis[n]=1;
     step[n]=0;
     Q.push(n);
     while(!Q.empty()){
     int s=Q.front();
     if(s==k)return ;
     Q.pop();
     if(check(s+1)&&!vis[s+1]){
     vis[s+1]=1;
     step[s+1]=step[s]+1;
     Q.push(s+1);
     }
     if(check(s-1)&&!vis[s-1]){
     vis[s-1]=1;
     step[s-1]=step[s]+1;
     Q.push(s-1);
     }
     if(check(s<<1)&&!vis[s<<1]){
     vis[s<<1]=1;
     step[s<<1]=step[s]+1;
     Q.push(s<<1);
     }
     } 
}
int main(){
    while(~scanf("%d%d",&n,&k)){
    if(n>=k){
    printf("%d\n",n-k);
    }
    else {
    memset(vis,false,sizeof vis);
    memset(step,0,sizeof step);
    bfs();
    printf("%d\n",step[k]);
    }    
    }

    return 0;
}
View Code

D - Fliptile(学到许多)

 POJ - 3279 

 题意: 给你一个01网格图,问你怎么按这些点,使得网格图全部置为0,找出字典序最小的方案;

解法: 这题用到二进制思想,解法就是枚举第一行的按键,然后剩下的每一行都可以由上一行递推而来;

递推公式为:press[i][j]=(mp[i-1][j]+press[i-1][j]+press[i-1][j+1]+press[i-2][j]+press[i-1][j-1])&1;

最后判断一下情况是否符合,即最后一行按键是否能全部熄灭,

这里我犯了一个错误,就是判断最后一行时没有按照递推公式算导致错误;

然后枚举第一行的按键用到了二进制,即枚举0<i<(1<<m),表示有m位二进制数,然后用位运算截取每一位数,即为 press[1][j]=(i>>(m-j))&1;

over

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std; 
int mp[20][20],ans[20][20],press[20][20];
int n,m;
const int inf=0x3f3f3f3f;
int guess(){

    for(int i=2;i<=n;i++){
    for(int j=1;j<=m;j++){
    press[i][j]=(mp[i-1][j]+press[i-1][j]+press[i-1][j+1]+press[i-2][j]+press[i-1][j-1])&1;
    }
    }
    for(int j=1;j<=m;j++){
    if((press[n][j]+mp[n][j]+
    press[n-1][j]+press[n][j-1]+press[n][j+1])&1!=0)
    return inf;
    }
    
    int cnt=0;    
    for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
    cnt+=press[i][j];
    }
    }

    return cnt;
}
int main(){
    
    while(~scanf("%d%d",&n,&m)){
    memset(mp,0,sizeof mp);
    memset(ans,0,sizeof ans);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&mp[i][j]);
    int res=inf;
    for(int i=0;i<(1<<m);i++){
    memset(press,0,sizeof press);
    int test=1;
    for(int j=1;j<=m;j++){
    press[1][j]=(i>>(m-j))&1;
    }
//    cout<<"test:"<<i<<" "<<guess()<<endl;
//    for(int i=1;i<=n;i++)
//    for(int j=1;j<=m;j++)
//    printf("%d%c",press[i][j],j==m?'\n':' ');
    
    int sum=guess();
    if(res>sum){
    res=sum;
    memcpy(ans,press,sizeof press);
    }
    }    
    if(res==inf)printf("IMPOSSIBLE\n");
    else {
//        cout<<"test:"<<endl;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    printf("%d%c",ans[i][j],j==m?'\n':' ');

    }
    }
    return 0;
}
View Code

E - Find The Multiple

 POJ - 1426 

题意:就是给你一个数,让你找它的一个只含01倍数的十进制数,

解法:直接深搜,但开始T了一发,没有做好剪树枝

计一个flag,如果搜到目标就停止;这样就能过了;

#include<cstdio>
using namespace std;
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
int n,flag;
ull ans;
void dfs(ull x,int step){
     if(flag)return ;
     if(x%n==0){
     flag=1;
     ans=x;
     return ;
     }
     if(step==19)return ;
     dfs(x*10+1,step+1);
     dfs(x*10,step+1);
}
int main(){
//    cout<<1000000000000000110%6<<endl;
    while(~scanf("%d",&n),n){
    ans=0,flag=0;
    dfs(1*1ll,1);
    printf("%lld\n",ans);
    }
    return 0;    
}
View Code

F - Prime Path

 POJ - 3126 

题意:就是给你两个素数,问你移动几步,使得由p走到q,而且每次移动只能变为素数,问你要变换几步;

解法:直接广搜即可;注意枚举每一位的情况;

#include<bits/stdc++.h>
#include<queue>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
const int maxn=1e5+5;
bool isprime[maxn];
void makeprime(){
     memset(isprime,true,sizeof isprime);
     isprime[1]=0;
     for(int i=2;i*i<=maxn;i++){
     if(isprime[i]==0)continue;
     for(int j=i*2;j<=maxn;j+=i){
     isprime[j]=0;
     }
     }
//     rep(i,1,maxn){
//     cout<<i<<" "<<isprime[i]<<endl;
//     }
}
struct point{int num,step;};
int ans,p,q;
bool vis[maxn]; 
bool  bfs(){
      memset(vis,0,sizeof vis);
     vis[p]=1;
     point tmp,next;
     tmp.num=p,tmp.step=0;
     queue<point>Q;
     Q.push(tmp);
     while(!Q.empty()){
     tmp=Q.front();
     Q.pop();
     vis[tmp.num]=1;
     if(tmp.num==q){
     ans=tmp.step;
     return true;
     } 
     int t=tmp.num;
     for(int i=0;i<4;i++){
     for(int j=0;j<=9;j++){
     t=tmp.num;
     if(i==0){
     t-=t%10;
     t+=j;
     }
     else if(i==1){
     t-=t%100/10*10;
     t+=j*10;
     }
     else if(i==2){
     t-=t%1000/100*100;
     t+=j*100;
     }
     else if(i==3){
     t-=t%10000/1000*1000;
     t+=j*1000;
     }
     if(t==tmp.num||t<1000||vis[t]||!isprime[t])continue;
     next.num=t,next.step=tmp.step+1;
     vis[t]=1;
     Q.push(next);
     }
     }
     
     }
     return false;
}
int main(){
    makeprime();
    int cas;
    scanf("%d",&cas);
    while(cas--){
    memset(vis,0,sizeof vis);
    scanf("%d %d",&p,&q);
    ans=0;
    if(bfs())printf("%d\n",ans);
    else printf("Impossible\n"); 
    
    }
    return 0;
}
View Code

 

G - Shuffle'm Up

 POJ - 3087 

题意:就是给你两堆牌,问你怎么洗才能洗到目标状态,如果洗不到,输出-1;

解法:模拟洗牌的过程,如果洗到初始的状态,说明洗不到,

这里我犯了一个错误,就是在在下标里面写位运算会出错,也不知tmp[i*2]=s2[i];道为什么;

tmp[i*2]=s2[i];  写成tmp【i>.>1】就不对了

#include<iostream>
#include<string>
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)k;i>=(int)j;i--)
using namespace std;
typedef long long ll;
string tmp,s1,s2,s12,goal;
int step,n,ans;
void bfs(){

     s1=tmp.substr(0,n);
     s2=tmp.substr(n,n);
     for(int i=0;i<n;i++){
     tmp[i*2]=s2[i];
     tmp[i*2+1]=s1[i];
     }
     step++;
     if(tmp.compare(goal)==0){
     ans=step;
     return ;
     }
       if(tmp.compare(s12)==0){
       ans=-1;
       return ;
       }
     bfs();  
}
int main(){
    int t,cas=1;
//    scanf("%d",&t);
    cin>>t;
    while(t--){
//    scanf("%d",&n);
    cin>>n>>s1>>s2>>goal;
    tmp=s1+s2;
    s12=tmp;
    ans=step=0;
    bfs();
    printf("%d %d\n",cas++,ans);
    }
    return 0;
}
View Code

 

待更新;

PS:还是太菜了,要坚持刷题,写博客;

posted @ 2020-02-14 00:12  无声-黑白  阅读(185)  评论(0编辑  收藏  举报