很久没有学习算法了,今天来看看数组吧。==
先复习一下C语言知识点:
1.scanf 和 gets :
都能接收字符串,假如char a[ 10 ],scanf("%s",&a); gets(a);
区别就是scanf不能接收空格,Tab,回车,只会输出空格之前的字符。gets能全部接收并用\0代替\n,回车键不会留在输入缓冲区中。比如输入abc fgh,用scanf只能输出abc ,用gets 输出的是abc fgh 。
2.printf 和 puts:
都能输出字符串,但是puts()在输出字符串时会将’\0’自动转换成’\n’进行输出,就是输出完字符串后会自动换行。
3.getchar 和 putchar:
这两个函数是用来获取和显示字符的,每次只能处理一个字符。如果想通过这两个函数实现输入多个字符:比如以#作为结束。
char ch;
ch=getchar();
while(ch!='#')
{
putchar(ch);
ch=getchar();
}
4.
比较大的数组应尽量声明在
main
函数外,否则程序可能无法运行。
5.
从数组
a
复制k
个元素到数组
b
,可以这样做:
memcpy
(
b,
a,sizeof(int)
*
k
);
#include <string.h> 全复制过去:memcpy(b,a,sizeof(a));
6.关于字符数组后边来讲。
7.memcpy 和 strcpy:
strcpy一般用于字符串的复制,遇到\0结束(会拷贝结尾的\0),没有指定长度,容易溢出。memcpy对复制类型没有限制,可以指定复制长度,更安全。
8.函数strchr (s,c)查找字符串s中首次出现字符c的位置,返回首次出现c的位置的指针,如果s中不存在c则返回NULL。
![](https://img-blog.csdnimg.cn/4bacf2432316432c89f3b422b67e86fd.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rOh6I-c6bG8MTEx,size_20,color_FFFFFF,t_70,g_se,x_16)
9.for循环里如果只有一条语句,可以不加大括号,多条语句必须加。双重循环外边的for里只有一个内循环语句,也可以不加,但是内循环里有多条语句的话必须加大括号。if 括号里如果不加的话只执行一条,我习惯都加大括号,但是答案里基本不加,看不懂所以查了查。
10. sprintf 函数:把整数打印到字符串中。sprintf(s, "%d", 123); //产生"123"。经常用来用来将整型转化为字符串。
3.1开灯问题
开灯问题。有
n
盏灯,编号为
1
~
n
。第
1
个人把所有灯打开,第
2
个人按下所有编号为
2的倍数的开关(这些灯将被关掉),第3
个人按下所有编号为
3
的倍数的开关(其中关掉的灯将被打开,开着的灯将被关闭),依此类推。一共有k
个人,问最后有哪些灯开着?输入n
和
k
,输出开着的灯的编号。
k
≤
n
≤1000
。
//0:灯打开
//1:灯关闭
#include <stdio.h>
#include <malloc.h>
int main()
{
int n,k,*list;//n盏灯 k个人
int i=0,j=0,t=0,m=0; //循环变量
scanf("%d%d",&n,&k);
list=(int*)malloc(sizeof(int)*n);
for(i=0;i<n;i++)
{
list[i]=1;//默认灯的状态
}
for(j=1;j<=k;j++)//第几个人
{
for(t=1;t<=n;t++)//灯的序号
{
if(t%j==0)
{
list[t]=!list[t];//灯的状态取反
}
}
}
for(m=1;m<=n;m++)
{
if(list[m]==0)
{
printf("%d ",m);
}
}
return 0;
}
注:!是逻辑取反,把非零变成0,0变成1。~是按位取反,把二进制中的0变成1,1变成0。
先保存一段代码:
#include <stdio.h>
#include <malloc.h>
int main()
{
int **p; //定义二维指针。
int m,n; //行数和列数。
int i,j;
scanf ("%d%d",&m,&n); //输入行数和列数。
p = (int **)malloc( sizeof(int*)*m); //申请一组一维指针空间。
for(i=0;i<m;i++)
p[i]=(int*)malloc(sizeof(int)*n);
for(i=0;i<m;i++)
for (j=0;j<n;j++)
scanf ("%d",&p[i][j]); //输入第i行第j列的数据。其中&p[i][j]也可以写作p[i]+j或者是 *(p+i) + j. 功能相同。
printf ( "输入的数组为%d行%d列:\n" , m, n);
for (i=0;i<m;i++)
{
for (j=0;j<n;j++) //这个循环可以输出一行元素。
printf ("%d ", p[i][j]); //输出i行j列的元素。
printf ( "\n" ); //每行输入结束加换行符。
}
//释放内存
for (i=0;i<m;i++)
free(p[i]);
free (p);
return 0;
}
3.2 蛇形填数
效果:
移动轨迹:从第0行,第n-1列开始,下下下,左左左,上上上,右右,下下,左,上。
先给x赋值0,给y赋值为n-1,然后马上把它们作为数组a的下标,可以合并成一行代码。
然后tot和a[0][n-1]都要赋值1,也可以合并完成。简洁,并且没有牺牲程序的可读性。
a[x+1][y]==0 可以写成 !a[x+1][y]。
#include <stdio.h>
#include <string.h>
#define maxn 20
int a[maxn][maxn];
int main()
{
int n,x,y,tot=0;
scanf("%d",&n);//输入阶数n
memset(a,0,sizeof(a));
tot=a[x=0][y=n-1]=1;
while(tot<n*n)
{
while(x+1<n&&!a[x+1][y]) a[++x][y]=++tot;
while(y-1>=0&&!a[x][y-1]) a[x][--y]=++tot;
while(x-1>=0&&!a[x-1][y]) a[--x][y]=++tot;
while(y+1<n&&!a[x][y+1]) a[x][++y]=++tot;
}
for(x=0;x<n;x++)
{
for(y=0;y<n;y++)
{
printf("%3d",a[x][y]);
}
printf("\r\n");
}
return 0;
}
3.3 竖式问题
找出所有形如
abc*de
(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一个空行。最后输出解的总数。具体格式见样例输出(为了便于观察,竖式中的空格改用小数点显示,但所写程序中应该输出空格,而非小数点)。
这个题的意思是:输入几个数,然后输出效果是这样的:为什么输入2357?得保证竖式中所有出现的数都在这个2357里面才行。
![](https://img-blog.csdnimg.cn/4378d9c3a6044b8ba4a894ed2a4134c4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rOh6I-c6bG8MTEx,size_14,color_FFFFFF,t_70,g_se,x_16)
#include <stdio.h>
#include <string.h>
int main()
{
int abc,de,x,y,z,i,ok=1;
int count=0;
char s[20],buf[99];
scanf("%s",s);
for(abc=111;abc<1000;abc++)
{
for(de=11;de<100;de++)
{
x=abc*(de%10);//第一层
y=abc*(de/10);//第二层
z=abc*de; //第三层
sprintf(buf,"%d%d%d%d%d",abc,de,x,y,z);//把整型打印到字符数组里
ok=1;
for(i=0;i<strlen(buf);i++)
if(strchr(s,buf[i])==NULL)//在s中查找buf[i]不存在
{
ok=0;
}
if(ok==1)//存在 输出
{
printf("<%d>\n",++count);
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n", abc, de, x, y, z);
}
}
}
printf("The number of solutions = %d\n", count);
return 0;
}
附上strchr的用法:
1.在这个题中,sprintf 输出到字符串(要保证字符串足够大),printf 输出到屏幕,fprintf 输出到文件(关于文件我记着前面的文章里提到过,但是没有学文件的操作);然后strchr函数是用来在一个字符串中查找单个字符的。
2.C语言中的字符串是以‘\0’结尾的,strlen(s)返回结束标记之前的字符个数,s[0]到s[strlen(s)-1]。
这个题我写的是这样的:
#include <stdio.h>
#include <string.h>
void main()
{
int abc,de,i,count1=0,flag=1;//三位数*两位数
int c1,c2,c3;//第一层 第二层 第三层(结果)
char s[20],buf[113];
scanf("%s",s);//输入数字集合
for(abc=111;abc<1000;abc++)
{
for(de=11;de<100;de++)
{
c1=abc*(de%10);
c2=abc*(de/10);
c3=abc*de;
memcpy(&buf[0],s,20);
memcpy(&buf[20],&abc,3);
memcpy(&buf[23],&de,2);
memcpy(&buf[25],&c1,4);
memcpy(&buf[29],&c2,4);
memcpy(&buf[33],&c3,5);
flag=0;
for(i=0;i<strlen(buf);i++)
{
if(strchr(s,buf[i])==NULL)
{
flag=1;
}
if(flag==0)
{
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d",abc,de,c1,c2,c3);
}
}
}
}
}
不对可能是memcpy复制过去的有空格所以就一直输出,个人感觉。。