简单搜索

题目大意是:你被困在一个地牢里,现在正在S点,需要走到E点。迷宫有多层,多行,多列,可以向上走,向下走,或者前后左右,求最短路,如果无法到达的话输出无解。这是一道经典的走迷宫问题,只不过从二维迷宫变成了拥有层数的三维迷宫,30^3的数据范围,用一般的深度优先搜索枚举路线是根本不可能了,所以使用广搜。只要将数组开成三维,方向增加了上下,增加了一个层数的队列。

include <stdio.h>

include <string.h>

define M 50

char dg[M][M][M];
int l, r, c;
int si, sj, sk, ei, ej, ek;
int min,flag,head,tail;
int book[M][M][M];
struct Q
{
int x; //记录地牢的坐标
int y;
int z;
int s; //记录步数
}que[30000];
void bfs()
{

int next[6][3] = {{0, 0, 1}, {0, 1, 0}, {0, 0, -1}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}}; // 方向数组
while(head < tail) //当队列不空时
{
	for (int k = 0; k < 6; k++)
	{
		int tx = que[head].x + next[k][0];
		int ty = que[head].y + next[k][1];
		int tz = que[head].z + next[k][2];
		if (tx < 0 || tx >= l || ty < 0 || ty >= r || tz < 0 || tz >= c) continue;
		if (book[tx][ty][tz] == 0 && dg[tx][ty][tz] == '.' || dg[tx][ty][tz] == 'E')
		 {
			book[tx][ty][tz] = 1; //标记访问过
			que[tail].x = tx;
			que[tail].y = ty;
			que[tail].z = tz;
			que[tail].s = que[head].s + 1;
			tail++;
		 }
		if (dg[tx][ty][tz] == 'E')
		 {
			printf("Escaped in %d minute(s).\n", que[tail-1].s);
			return 0;
		 }
	}
	head++;
}
printf("Trapped!\n");

}

int main()
{
while (scanf("%d %d %d", &l, &r, &c), (l || r || c))
{
int i, j, k;
flag = 0;
memset(book, 0, sizeof(book));
memset(que, 0, sizeof(que));
for (i = 0; i < l; i++)
{
for (j = 0; j < r; j++)
{
scanf("%s", dg[i][j]);
for (k = 0; k < c; k++)
{
if (dg[i][j][k] == ‘S’)
{
si = i;
sj = j;
sk = k;
book[i][j][k] = 1;
}
}
}
}
head = 1; //队列头部
tail = 1; //队列尾部
que[head].x = si;
que[head].y = sj;
que[head].z = sk;
que[head].s = 0;
tail++;
book[si][sj][sk] = 1;
bfs();
}
return 0;
}

题意:输入N和K,其中N代表起点,K代表终点,N每次只能+1,-1或者 x 2,问至少需要几步才能得到K。 (0 ≤ N,K ≤ 100,000)
例如输入5 17,那么最少步数为4。走的方法为:(1)5 x 2=10 ,(2)10-1=9,(3)9 x 2 =18,(4)18-1=17
思路:只需利用BFS遍历起点能到达的点,再由这些点遍历到其他点,当遍历到终点时即可。
#include<stdio.h>
#define N 100005
int u,v,x,f[N],q[N2],h,t;
int main()
{
scanf("%d%d",&u,&v);
q[t++]=u;
while(h<t)
{
u=q[h++];
if(u==v)
{
printf("%d\n",f[u]);
break;
}
x=f[u]+1;
if(u>0&&!f[u-1])
f[q[t++]=u-1]=x;
if(u<=N&&!f[u+1])
f[q[t++]=u+1]=x;
if(u
2<=N&&!f[u2])
f[q[t++]=u
2]=x;
}
}
题意:有一个nm的范围摆满了瓦片。 瓦片有黑白两面, 1代表黑色, 0 代表白色。 每次点击一个瓦片的时候, 它和它上下左右的5个瓦片都会同时反转过来。 问点击次数最小的点集方法(存在点集次数相同的不同方法的时候, 输出字典序最小的), 如果不存在输出IMPOSSIBLE
思路:枚举第一行的状态,然后根据依次遍历n
m,当前行是否翻转取决于它的上一行的状态,最后检查最后一行是否全是0即可。
(同一个格子翻转两次是无用的,并且翻转次序也无用)
#include <stdio.h>
#include <string.h>
#define INF 0xfffffff
#define ll long long
#define N 20

int n, m;
int res;
int ori[N][N]; //原始状态
int ans[N][N]; //保存答案
int tmp[N][N]; //翻转模拟数组
int vis[N][N]; //记录是否翻转

void flip(int i, int j)
{
tmp[i][j] ^= 1;
if (i - 1 >= 0)
tmp[i - 1][j] ^= 1;
if (i + 1 < n)
tmp[i + 1][j] ^= 1;
if (j - 1 >= 0)
tmp[i][j - 1] ^= 1;
if (j + 1 < m)
tmp[i][j + 1] ^= 1;
}

int solve()
{
for (int i = 1; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (tmp[i - 1][j])
{
flip(i, j);
vis[i][j] = 1;
}
}
}
int time = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
time += vis[i][j];
if (tmp[i][j])
return INF;
}
}
return time;
}
int main()
{
int i, j, k;
while (scanf("%d%d", &n, &m) != EOF)
{
res = INF;
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
scanf("%d", &ori[i][j]);
}
}
int MAX = 1 << m;
for (k = 0; k < MAX; k++) //枚举第一行的状态
{
memset(vis, 0, sizeof(vis));
memcpy(tmp, ori, sizeof(ori));
for (j = 0; j < m; j++)
{
if (k & (1 << j))
{
flip(0, j);
vis[0][j] = 1;
}
}
int time = solve();
if (time < res)
{
res = time;
memcpy(ans, vis, sizeof(vis));
}
}
if (res == INF) printf(“IMPOSSIBLE\n”);
else
{
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
if (j)
printf("’ '\n");
printf("%d\n",ans[i][j]);
}
printf("\n");
}
}
}
return 0;
}

题意:给定一个正整数n,编写一个程序来找出一个n的非零倍数m,它的十进制表示只包含数字0和1。你可以假设n不大于200,对应的m不超过100位小数。
思路:在存储二进制时使用了二叉树结构,每一次的试探都不同,直接用下表2i与2i+1区分开了两个儿子节点,因此每一次都搜索这个数的下一个位置上为1还是为0才可以被n整除,为了得到父亲节点,每一次都是i/2,以此类推。
最后在搜索到这个二进制数后(满足搜索条件,这个二进制数的十进制形式能够整除n),就退出,并通过二叉树的性质回溯出这个二进制数。借鉴了他人的思想,学到了,很棒!
//找到一个能整除n的二进制数(形式上是二进制,而计算时当作十进制)
//i%2既可以便利到二进制的每一种情况 0 1 ,利用了同余模定理
//这里需要注意运用到了树形结构 ,树根从下标1开始,后面
//输出时为倒序,因此ans位决定了最后一位结果为0还是1,而最高位一定为1

#include<stdio.h>
#include<string.h>
int result[1000005];
int r[210];
int n;
int main()
{
while(scanf("%d",&n) != EOF){
if(n == 0) return 0;
int ans = 1;
result[1] = 1;
for(ans = 2;result[ans-1] != 0;ans++)
{ //找到一个能整除n的二进制数(形式上是二进制,而计算时当作十进制)
result[ans] = (result[ans / 2] * 10 + ans % 2 ) % n; //i%2既可以便利到二进制的每一种情况 0 1 ,利用了同余模定理
//这里需要注意运用到了树形结构 ,树根从下标1开始,后面
}
ans–;
int k = 0;
while(ans)
{ //输出时为倒序,因此ans位决定了最后一位结果为0还是1,而最高位一定为1
int a = ans % 2;
r[k] = a; //倒序存储
k++;
ans /= 2;
}
for(int i = k-1;i >= 0;i–)
{
printf("%d",r[i]);
}
puts("");
}
return 0;
}
搜索二叉树即可,每个素数即为一个节点,可直接变换的两个素数之间有边,依此来搜索。

#include <stdio.h>
#include <string.h>
int su[10000],a[10000],b[10000];
int n,m;
void Begin()
{
    int i,j;
    su[0]=su[1]=1;
    for (i=2;i<100;i++)
        if (su[i]==0)
            for (j=ii;j<10000;j+=i) su[j]=1;
}
void Work()
{
    int i,x,t,t1;
    scanf("%d%d",&n,&m);
    memset(a,0,10000
sizeof(int));
    a[n]=1;
    b[0]=n;
    t=0;
    t1=1;
    while (a[m]==0&&t<t1)
    {
        x=b[t]/1010;//直接构造关于当前数的其他40个四位数(有四个是其本身)
        for (i=0;i<10;i++)
            if (su[x+i]==0&&a[x+i]==0){a[x+i]=a[b[t]]+1;b[t1++]=x+i;}
        x=b[t]/100
100+b[t]%10;
        for (i=0;i<10;i++)
            if (su[x+10i]==0&&a[x+10i]==0){a[x+10i]=a[b[t]]+1;b[t1++]=x+10i;}
        x=b[t]/10001000+b[t]%100;
        for (i=0;i<10;i++)
            if (su[x+100
i]==0&&a[x+100i]==0){a[x+100i]=a[b[t]]+1;b[t1++]=x+100i;}
        x=b[t]%1000;
        for (i=1;i<10;i++)
            if (su[x+1000
i]==0&&a[x+1000i]==0){a[x+1000i]=a[b[t]]+1;b[t1++]=x+1000*i;}
        t++;
    }
    if (a[m]==0) printf(“Impossible\n”);
    else printf("%d\n",a[m]-1);
}
int main()
{
    int n1;
    Begin();
    scanf("%d",&n1);
    for (;n1>0;n1–) Work();
    return 0;
}
判断的方式是只要在洗牌的过程中出现上一次和这一次牌的顺序相同,那么他就一定会一直无限循环下去,永远得不到题目要求所需的排牌序。

#include<stdio.h>
#include<string.h>

char s[1000][100];
int main()
{
int T,t=1;
char s1[1000];
char s2[1000];
char k[1000];
scanf("%d",&T);
while(T–)
{
int m,flag=0;
scanf("%d",&m);
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
memset(s,0,sizeof(s));
memset(k,0,sizeof(k));
scanf("%d",&s1);
scanf("%d",&s2);
scanf("%d",&k);
for(int i=0;;i++)
{
for(int j=0;j<m;j++)
{
s[i][2j]=s2[j];
}
for(int j=0;j<m;j++)
{
s[i][2
j+1]=s1[j];
}
if(strcmp(s[i],k)==0)
{
printf("")cout<<t<<" “<<i+1<<endl;
break;
}else
{
for(int j=0;j<i;j++)
{
if(strcmp(s[i],s[j])==0)
{
cout<<t<<” "<<-1<<endl;
flag=1;
break;
}
}
if(flag)
{
break;
}
}
for(int j=0;j<m;j++)
{
s1[j]=s[i][j];
s2[j]=s[i][j+m];
}

    }
    t++;
}
return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值