scanf("%d%d",&a,&b);//读取键盘输入并放到int类型a和b中
scanf("%s",s);//遇到空格或者TAB键表示该语句执行完毕,s为一个char数组
int c=getchar();//读入单个字符,使用putchar(c)输出
scanf("%lf%lf", &r, &h);//lf对应的是double类型
sscanf(&s[i],"%d",&v);//以数组s为输入源,输入源为一个指针,看成一个从i位置开始的字符串,赋值给v,适用于在字符串数组中取数字
printf("%d\n",8/5);//表示整数部分(整数除法)
printf("%04d\n",a);//按照4位数打印,不足补零,超出按原样输出
printf("%4d\n",a);//按照4位数打印,不足补空格,超出按原样输出
printf("%.1f\n",8.0/5.0);//表示保留一位小数部分(实数除法)
printf("%f\n",8.0/5.0);//表示实数
sprintf(buf,"%d",x)//输出到数组buf中
scanf("\n")//读取换行符,此时输入流中就没有换行了
fgets(&s[i],maxn,stdin)//从标准输入流中获取最多maxn-1个数据到数组s中,空白符换行符也包括在内,读到换行符就不再继续读了。
优先级:
解引用的优先级比正负号高;
#include<cmath>
sqrt(3)//数学函数计算算术平方根
acos(-1.0)//cos pi = -1,求出pi值
double ceil(double x);//向上取整
double ceil(double x);//向下取整
#include<algorithm>
sort(a,a+10);//左闭右开区间的元素排序
max_element(a,a+10);//左闭右开区间的最大元素的地址
upper_bound(a,a+10,x);//左闭右开区间的最后的已排序好的元素的下一个地址,
lower_bound(a,a+10,x);//左闭右开区间的第一个已排序好的元素地址,大于等于他的第一个地址
atan2(y,x);//一到四象限的极角,[-pi,pi]
#include<cctype>
isalpha(ch)//判断是否是大写字母,大写返回2小写返回1非字母返回0
isspace(ch)//判断是否是空白或空格或制表符,是的话返回非零值
islower(ch)//判断小写
isupper(ch)//判断大写
#include<cstring>
strchr(s,buf[i])//在字符串s中查找buf[i]是否存在,返回该字符的地址
strcmp(s,"()")//若字符串与s相等就返回0
strlen(buf);//数组buf的元素个数
memset(a,0,sizeof(a));//a为一个数组
memcpy(a,b,sizeof(a));//a b 都是数组
#include<string>
string line;
getline(cin, line)//循环用字符串读取一行数字,注意空格也读进去了,遇到换行就返回真
#include<sstream>
string line,cmd;
stringstream ss(line);//读取line到ss流,ss就相当于cin,与getline配合使用
ss>>cmd;//输入到cmd中,遇到空白符停止输入,输入完毕以后ss中就没有相应的字符了
#include<map>
string s;
map<string,int> x;
x.count(s);//统计容器与关键字s相等的个数
#include<vector>
vector<int> x;
x.size();//统计容器中个数
#include<set>
x.clear();//使用之前的情况操作
x.count(s);//统计容器与关键字即值s相等的个数
x.insert(s);//插入操作
x.erase(s);//删除操作
x.erase(it++)//删除操作
it=x.find(s);//返回c的地址
vector<Cell> P;//当为类类型时,必须在结构体里定义小于号,因为set容器是按照大小排列的。
struct Cell {
int x, y;
Cell(int x=0, int y=0):x(x),y(y) {};
bool operator < (const Cell& rhs) const {
return x < rhs.x || (x == rhs.x && y < rhs.y);
}
};
#include<queue>
priorit_queue<int,vector<int>,greater<int>> q;//优先队列,有序排序队列,最小元素排在队列前面
q.top();//访问第一个元素,但不删除
q.pop();//移除第一个元素
q.push(ans);//插入元素,根据ans大小插入,不一定插入在队尾
datain为自己建立的txt文件,dataout为程序建立的,datain文件重命名不需要后缀.txt
本例程为寻找输入一组整数的最小值、最大值、平均值。
#include<stdio.h>
#define INF 10000
int main()
{
freopen("datain.txt","r",stdin);
freopen("dataout.txt","w",stdout);
int x,n=0,min=INF,max=-INF,s=0;
while(scanf("%d",&x)==1)
{
s+=x;
if(x<min) min=x;
if(x>max) max=x;
n++;
}
printf("%d %d %.3f\n",min,max,(double)s/n);
return 0;
}
#include<stdio.h>
#define INF 10000
int main()
{
FILE *fin, *fout;
fin=fopen("input.txt","rb");
fout=fopen("output.txt","wb");
int x,n=0,min=INF,max=-INF,s=0;
while(fscanf(fin,"%d",&x)==1)
{
s+=x;
if(x<min) min=x;
if(x>max) max=x;
n++;
}
fprintf(fout,"%d %d %.3f\n",min,max,(double)s/n);
fclose(fin);
fclose(fout);
return 0;
}
输入结束后,先按enter键,再ctrl+z,最后按enter键。
#include<stdio.h>
#define INF 10000
int main()
{
int x,n=0,min=INF,max=-INF,s=0;
while(scanf("%d",&x)==1)
{
s+=x;
if(x<min) min=x;
if(x>max) max=x;
n++;
}
printf("%d %d %.3f\n",min,max,(double)s/n);
return 0;
}
n=0为结束标记
输入:
8
2 8 3 5 1 7 3 6
4
4 6 10 0
0
#include<stdio.h>
#define INF 10000
int main()
{
int x,n=0,kase=0;
while (scanf("%d",&n)==1&&n)
{
int min=INF,max=-INF,s=0;
for(int i=0;i<n;i++)
{
scanf("%d",&x);
if(x<min)min=x;
if(x>max)max=x;
s+=x;
}
if(kase)printf("\n");
printf("Case %d: %d %d %.3f",++kase,min,max,(double)s/n);
}
return 0;
}
设鸡有x只,兔有y只,则x+y=n,2x+4y=m,求x y 即可
#include<iostream>
using namespace std;
int main()
{
int n,m,x,y;
scanf("%d%d",&n,&m);
y=(m-2*n)/2;
x=n-y;
if(m%2==1|| x<0 || y<0)
printf("No answer\n");
else
printf("%d %d\n",x,y);
return 0;
}
n盏灯,第一个人把所有的灯打开,第二个人把编号为2的倍数的灯开关按下,第三个人把编号为3的倍数的开关按下,一共k个人,问最后哪些灯开着,k<=n<=1000。
开关问题就是取反操作的问题,倍数问题就是取余操作的问题。
要求输出第一个没有空格中间空格间隔最后元素没有空格。
#include<stdio.h>
#include<string.h>
#define INF 1010
int op[INF];
int main()
{
int n,k,flag=1;
memset(op,0,sizeof(op));//数组置零
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)
for (int j=1;j<=n;j++)
if(j%i==0)
op[j]=!op[j];
for(int i=1;i<=n;i++)
if(op[i])
{
if(flag)//标志量,设置第一个元素前和最后一个元素尾没有空格,之间有全部有空格
flag=0;
else printf(" ");
printf("%d",i);
}
printf("\n");
return 0;
}
在一个n x n 的矩阵里,形如
7 8 1
6 9 2
5 4 3
从右上角开始赋值,不断向下走然后向左,然后向上,然后向右,继续第二层循环,填数原则为:先判断再移动。判断是否越界并且是否已经填过数。
#include<stdio.h>
#include<string.h>
#define INF 10
int op[INF][INF];
int main()
{
int n,x,y,tot=0;
scanf("%d",&n);
memset(op,0,sizeof(op));
tot=op[x=0][y=n-1]=1;
while(tot<n*n)
{
while(x+1<n && !op[x+1][y]) op[++x][y]=++tot;//保证不越界并且下一个元素未被填入时循环
while(y-1>=0 && !op[x][y-1]) op[x][--y]=++tot;
while(x-1>=0 && !op[x-1][y]) op[--x][y]=++tot;
while(y+1<n && !op[x][y+1]) op[x][++y]=++tot;
}
for(x=0;x<n;x++)
{
for(y=0;y<n;y++)
printf("%3d",op[x][y]);
printf("\n");
}
return 0;
}
三位数乘两位数的算式,列出竖式,要求输入数字集合不含空格,在竖式中找出满足该集合的所有竖式,采用字符串数组保存输入数字,遍历所有的三位数与两位数相乘,求出满足条件的竖式打印出来。(句号代表空格)
输入:2357
输出:
<1>
。。 775
X 。。33
。。。。。
。2325
2325。
。。。。。
25575
解决方案:1
#include<stdio.h>
#include<string.h>
int main()
{
int count=0;
char s[20],buf[99];
scanf("%s",s);//读入字符串到字符数组
for(int abc=100;abc<=999;abc++)
for(int de=10;de<=99;de++)
{
int x=abc*(de%10),y=abc*(de/10),z=abc*de;
sprintf(buf,"%d%d%d%d%d",abc,de,x,y,z);
int ok=1;
for(int i=0;i<strlen(buf);i++)//strlen为字符串数组实际长度,不包括结束符
if(strchr(s,buf[i])==NULL) ok=0;//字符比较
if(ok)
{
printf("<%d>\n",++count);
printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n",abc,de,x,y,z);
}
}
printf("解决方案:%d\n",count);
return 0;
}
把一段英语中的引号,改为正确的左引号与右引号;
因为引号是成对出现的,所以只需要将第一个引号改为左引号,第二个引号不变,第三个引号改为左引号,以此类推即可。
因为引号只是一个字符,可以对他进行边读边处理,不需要把完整输入的字符串完整地存下来。使用getchar()函数即可。该函数返回一个int值,遇到EOF才会停止。
#include<stdio.h>
int main()
{
int c,f=1;
while((c=getchar())!=EOF)
{
if(c=='"')
{
printf("%s",f?"``":"''");
f=!f;
}
else printf("%c",c);
}
return 0;
}
输入的字符总是比在键盘实际中的位置的向右一个,要进行归位,使用getchar()边读边处理,在while中他遇到换行会执行语句但是不会停止读入,我们可以定义一个常量数组保存键盘位置的值,遍历找到错误的这些值进行修正。
#include<stdio.h>
#include<stdio.h>
int main()
{
int i, c;
char k[]={"`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./"};
while((c=getchar())!=EOF)
{
for(i=1;k[i]&&k[i]!=c;i++) ;//遍历数组直到找到输入数据
//若没有找到数据到结束符停止
//循环体什么指令也不执行,只是寻找i的数值
//这里的i必须定义在for循环外部
if(k[i])putchar(k[i-1]);
else putchar(c);
}
return 0;
}
回文串就是正读反读一个样,镜像串就是取完他的镜像以后才是个回文串。
在一组数据中先判断是否是回文串,再判断是否是镜像串即可。。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
const char *rev="A 3 HIL JM O 2TUVWXY51SE Z 8 ";//一共26+9个字符,包含空格
const char *msg[]={"不是回文串","是回文串","是镜像串","是回文镜像串"};
char r(char ch)//将字符转换为镜像
{
if(isalpha(ch))return rev[ch-'A'];//找到大写字母所在的下标
return rev[ch-'0'+25];//找到数字所在的下标,从第26号开始
}
int main()
{
char s[30];
while(scanf("%s",s)==1)
{
int f1=1,f2=1;
int len=strlen(s);
for(int i=0;i<(len/2);i++)
{
if(s[i]!=s[len-1-i])f1=0;//必须检测不等,有一个不等成立就不是回文串,但若要检测相等必须是全部成立才能证明是回文串
if(r(s[i])!=s[len-1-i])f2=0;
}
printf("%s -- %s.\n\n",s,msg[f1+2*f2]);
}
return 0;
}
给定数组序列,与答案序列进行对比,求出与答案序列位置相同的个数,再求出在两个序列都出现过的数字但位置不同的个数。
求位置不同的个数时,先求出两个序列中所有数值出现的个数,取出他们当中最小的个数,然后再减去位置相同的个数即可。
#include<stdio.h>
#define max 1010
int a[max],b[max];
int main()
{
int n,count=0;
while(scanf("%d",&n)==1 && n)
{
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(;;)
{
int A=0,B=0;
for(int i=0;i<n;i++)//寻找与答案序列相同的个数
{
scanf("%d",&b[i]);
if(a[i]==b[i])A++;
}
if(b[0]==0)break;
for(int d=1;d<=9;d++)//寻找与答案序列不同但是该序列在答案序列中出现过的个数
{
int c1=0,c2=0;
for(int i=0;i<n;i++)//统计序列中各数值出现的个数
{
if(b[i]==d)c1++;
if(a[i]==d)c2++;
}
if(c1>c2)B+=c2;else B+=c1;
}
printf("Game %d:\n",++count);
printf(" (%d,%d)\n",A,B-A);
}
}
return 0;
}
比如数字198,198+1+9+8=216就说198是216的生成元,现给定一个数,反求他的生成元。
采用枚举法求出生成元的所有集合。
#include<stdio.h>
#include<string.h>
#define max 100005
int a[max];
int main()
{
int n;
memset(a,0,sizeof(a));
for(int i=1;i<max;i++)
{
int y=i,x=i;
while(x>0){y+=x%10;x/=10;}//不知道i的位数,采用循环每次除10去掉一位直至为0退出循环
//注意一个数的生成元不只是一个,求最小生成元需要加条件,如果生成元已经确定好数字了就不需要在赋值新的生成元了
if(a[y]==0)
a[y]=i;
}
scanf("%d",&n);
printf("%d",a[n]);
return 0;
}
找出一个环状序列的最小字典序。
通过比较序列的起始位置谁大谁小,然后如果起始位置相等再依次比较后面的位置,对后面位置的比较主要是通过对位置 mod 序列长度达到循环遍历。
#include<stdio.h>
#include<string.h>
#define max 105
char a[max];
int less(const char *s,int p,int q)
{
int n=strlen(s);
for(int i=0;i<n;i++)
{
if(s[(p+i)%n]!=s[(q+i)%n])
return s[(p+i)%n]<s[(q+i)%n];
}
return 0;//相等
}
int main()
{
int f=0;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",a);
int n=strlen(a);
for(int i=1;i<n;i++)
if(less(a,i,f))f=i;
for(int i=0;i<n;i++)
putchar(a[(f+i)%n]);
printf("\n");
}
return 0;
}
素数只能被1和它本身整除,大于1的数字,对其进行遍历如果可以被别的数整除就不是素数,只需要遍历到它的算术平方根就行了,因为一个数写成两个数相乘的话,一定有一个数比它的算术平方根小或者相等。
#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
int isprim(int m)
{
if(m<=1)return 0;
int f=floor(sqrt(m)+0.5);
for(int i=2;i<=f;i++)
if(m%i==0)
return 0;
return 1;
}
int main()
{
int n;
scanf("%d",&n);
if(isprim(n))
cout<<"is";
else
cout<<"is not";
return 0;
}
给定一串字符,输入猜测字符串,全部猜中结束,最多只能猜六次。
先写框架,再写调用函数,自顶向下。
#include<stdio.h>
#include<string.h>
int chance,left,win ,lose;//因为调用函数也会使用到,所以变为全局变量
#define maxn 100
char s[maxn],s2[maxn];
void guess(char ch)
{
int f=1;
for(int i=0;i<strlen(s);i++)
{
if(ch==s[i]){left--;s[i]=' ';f=0;}
}
if(f)--chance;
if(!chance)lose=1;
if(!left)win=1;
}
int main()
{
int rnd;//回合数
while(scanf("%d%s%s",&rnd,s,s2)==3 && rnd!=-1 )
{
printf("round: %d\n",rnd);
win=lose=0;
left=strlen(s);
chance=7;
for(int i=0;i<strlen(s2);i++)
{
guess(s2[i]);
if(win || lose)break;
}
if(win)printf("WIN.\n");
else if(lose) printf("LOSE.\n");
else printf("CHICKEND.\n");
}
return 0;
}
对于在下标为1的数组进行遍历,需要对起始位置加上序列长度减一 mod 序列长度 然后再加1,要求位置不能为负;
#include<stdio.h>
#include<string.h>
int chance,left,win ,lose;//因为调用函数也会使用到,所以变为全局变量
#define maxn 25
int a[maxn];
int n,k,m;//n代表总数,k代表顺时针步长,m代表逆时针步长
int go(int p,int d,int s)
{
while(s--)
{
do{p=(p+d+n-1)%n+1;}while(a[p]==0);
}
return p;
}
int main()
{
while(scanf("%d%d%d",&n,&k,&m)==3 && n)
{
for(int i=1;i<=n;i++)a[i]=i;
int p1=n,p2=1;//当前位置
int left=n;//剩余人数
while(left)
{
p1=go(p1,1,k);
p2=go(p2,-1,m);
printf("%3d",p1);left--;
if(p2!=p1){printf("%3d",p2);left--;}
a[p1]=a[p2]=0;
if(left)printf(",");
}
printf("\n");
}
return 0;
}
使用getchar()读取字符,要求遇到换行时还能继续读取,在无限循环中需要加判断条件,如果遇不到换行并且也遇不到回车时就返回,对于二进制转换为十进制的公式为:
v=2*v+ch(ch为当前读入的数字,v初始化为0,读多少位循环多少次);
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=10000;
int main()
{
int a[maxn];//存放数字的数组
int n,q,x,kase=0;//n个数字,q个问题,找数字中的x在排序好的哪个位置
while(scanf("%d%d",&n,&q)==2&&n)
{
printf("Case %d",++kase);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
sort(a,a+n);
while(q--)
{
scanf("%d",&x);
int p=lower_bound(a,a+n,x)-a;//寻找大于等于x的第一个数字位置,类型为指针
if(a[p]==x)printf("%d found at %d\n",x,p+1);
else printf("Not found\n");
}
}
return 0;
}
#include<cstdio>
#include<string>
#include<sstream>
#include<set>
#include<iostream>
using namespace std;
int main()
{
set<string> dict;//集合,有很多元素
string s,buf;
while(cin >>s)
{
for(int i=0;i<s.length();i++)
if(isalpha(s[i]))s[i]=tolower(s[i]);else s[i]=' ';
stringstream ss(s);//ss为字符串流
while(ss>>buf)dict.insert(buf);
}
for(set<string>::iterator it=dict.begin();it!=dict.end();it++)
{
cout<<*it<<"\n";
}
return 0;
}
判断一个字符串是否可以重排以后得到另一个字符串,采用的方法是不考虑两个字符串的位置,先对其进行字典顺序排列,然后观察是否相等即可。
map可以像使用数组一样直接进行初始化,m[key]=value。
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
string rep(string &s)
{
string temp=s;
for(int i=0;i<temp.length();i++)
temp[i]=tolower(temp[i]);
sort(temp.begin(),temp.end());
return temp;
}
int main()
{
vector<string> word;
map<string,int> wordnum;
string s;
int x=0;
while(cin>>s)
{
if(s[0]=='#')break;
word.push_back(s);
string afters=rep(s);
if(!wordnum.count(afters))
wordnum[afters]=0;
wordnum[afters]++;
}
vector<string> temp;
for(int i=0;i<word.size();i++)
{
if(wordnum[rep(word[i])]==1)temp.push_back(word[i]);//如果重排以后的字母个数只有一个符合条件放入新的容器
}
sort(temp.begin(),temp.end());
for(int i=0;i<temp.size();i++)
cout<<temp[i]<<"\n";
return 0;
}
STL的stack头文件提供了栈,用stack< int >s的方式定义,用push()和pop()实现元素的入栈和出栈操作,top()取出栈顶元素但不删除。
STL的queue头文件提供了栈,用queue< int >s的方式定义,用push()和pop()实现元素的入队和出队操作,front()取出队首元素但不删除。
STL的queue头文件提供了栈,用priority_queue< int >s的方式定义,用push()和pop()实现元素的入队和出队操作,top()取出队首元素但不删除。
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=100;
const int maxcol=60;
string word[maxn];
void print(const string &s,int len,char extra)//采用传引用是为了避免不必要的复制
{
cout<<s;
for(int i=0;i<len-s.length();i++)
cout<<extra;
}
int main()
{ int n;
while(cin>>n)
{
int m=0;
for(int i=0;i<n;i++)
{
cin>>word[i];
m=max(m,(int)word[i].length());//必须强转为int类型,因为第二个参数是无符号类型的
}
int cols=(maxcol-m)/(m+2)+1,rows=(n-1)/cols+1;
print("",60,'-');
cout<<"\n";
sort(word,word+n);
for(int r=0;r<rows;r++)
{
for(int c=0;c<cols;c++)
{
int idx=c*rows+r;
if(idx<n)print(word[idx],c==cols-1?m:m+2,' ');
}
cout<<"\n";
}
}
return 0;
}
用数组s[i]代表插入位置i的内容,用数组next[i]代表插入位置i的下一个位置,用变量cur代表当前位置。这些位置代表的是数组s中的位置,并不代表实际链表的位置,实际链表中的位置由数组next确定。
本题由“【”表示从起点开始插入,“】”表示从“【”操作之前的位置开始插入。所以需要新增一个变量last来保存“【”之前的位置。
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=100005;
char s[maxn];
int next[maxn];
int main()
{
while(scanf("%s",s+1)==1)
{
int cur=0,last=0,n=strlen(s+1);
next[0]=0;//初始化零位置指向的下一个位置为虚拟位置0
for(int i=1;i<=n;i++)
{
char ch=s[i];
if(ch=='[')cur=0;
else if(ch==']')cur=last;
else
{
next[i]=next[cur];
next[cur]=i;
if(cur==last)last=i;//保存原来之前操作的位置
cur=i;
}
}
for(int i=next[0];i!=0;i=next[i])
printf("%c",s[i]);
printf("\n");
}
return 0;
}
用数组s[i]表示第i位置的内容,left[i]表示第i位置左边的位置,right[i]表示第i位置右边的位置。
双向链表的每一次相邻间元素互换操作都会产生三次链表节点的连接操作,不相邻元素间互换操作会产生四次链表节点的连接操作。
双向链表的每一次元素移动操作都会产生三次链表节点的连接操作。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 5;
int n, left[maxn], right[maxn];
//连接两个节点操作
inline void link(int L, int R) {
right[L] = R; left[R] = L;
}
int main() {
int m, kase = 0;
while(scanf("%d%d", &n, &m) == 2) {
建立初始化链表
for(int i = 1; i <= n; i++) {
left[i] = i-1;//头部指向的前一个位置为虚拟位置0
right[i] = (i+1) % (n+1);//尾部指向的下一个位置为虚拟位置0
}
right[0] = 1; left[0] = n;
int op, X, Y, inv = 0;
while(m--) {
scanf("%d", &op);
if(op == 4) inv = !inv;
else {
scanf("%d%d", &X, &Y);
if(op == 3 && right[Y] == X) swap(X, Y);
if(op != 3 && inv) op = 3 - op;
非法操作
if(op == 1 && X == left[Y]) continue;
if(op == 2 && X == right[Y]) continue;
int LX = left[X], RX = right[X], LY = left[Y], RY = right[Y];
if(op == 1) {
link(LX, RX); link(LY, X); link(X, Y);
}
else if(op == 2) {
link(LX, RX); link(Y, X); link(X, RY);
}
else if(op == 3) {
如果是相邻元素三次链表节点连接,不是就为四次
if(right[X] == Y) { link(LX, Y); link(Y, X); link(X, RY); }
else { link(LX, Y); link(Y, RX); link(LY, X); link(X, RY); }
}
}
}
int b = 0;
long long ans = 0;
for(int i = 1; i <= n; i++) {
b = right[b];
if(i % 2 == 1) ans += b;
}
if(inv && n % 2 == 0) ans = (long long)n*(n+1)/2 - ans;
printf("Case %d: %lld\n", ++kase, ans);
}
return 0;
}
问题描述:小球从根节点开始下落,每个节点初始值为0,小球每经过一个节点就把该节点的值反转,最后落到叶子节点,问第i个小球落到叶子结点的编号。
分析:小球每经过一个节点的时候,如果小球的编号是奇数它就向左走,偶数向右走,所以可以通过更新小球经过一个节点时的编号来判断该向哪走。小球如果是奇数,那么他经过一个节点以后更新值为x=(x+1)/2,偶数为x=x/2。
#include<cstdio>
using namespace std;
int main()
{
int I,d,n;
scanf("%d",&n);
while(n--)
{
int k=1;
scanf("%d%d",&d,&I);
for(int i=0;i<d-1;i++)
{
if(I%2){k=2*k;I=(I+1)/2;}
else{k=2*k+1;I=I/2;};
}
printf("%d\n",k);
}
return 0;
}
二叉树的节点定义为一个结构体,因为二叉树是递归定义的,所以节点的左右子节点为指向节点类型的指针。节点的内容由动态内存分配出来,节点只是一个地址,根节点的地址在全局变量给出,通过循环建立出自定义的节点位置,然后先把根节点放入FIFO队列中,然后依次取出节点,队列取出一个节点的时候把他的左右子节点放入队列里面,直至队列里面没有节点。
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=300;
char s[maxn];
struct NODE
{
int val;
bool valhave;
NODE *left , *right;
NODE():valhave(false),left(NULL),right(NULL){}//构造函数
};
NODE *root;
NODE *newnode(){return new NODE();}//申请新的节点并且执行构造函数
void addnode(int v,char *s)
{
int len=strlen(s);
NODE* u = root;//root根节点只有一个,可以把根节点的地址复制给一个副本
for (int i=0;i<len;i++)
{
if(s[i]=='L'){
if(u->left==NULL)
u->left=newnode();
u=u->left;
}
else if(s[i]=='R'){
if(u->right==NULL)
u->right=newnode();
u=u->right;
}
}
if(!u->valhave){u->val=v;u->valhave=true;}
}
void removetree(NODE *u)
{
if(u==NULL) return;
removetree(u->left);
removetree(u->right);
delete u;
}
bool readinput()
{
removetree(root);
root=newnode();//建立根节点,根节点的地址在全局变量中,地址一直存在,只是他指向的内容为空,new就会为他分配内容
for(;;){
if((scanf("%s",s)!=1)) return false;
if (!strcmp(s,"()"))break;
int v;
sscanf(&s[1],"%d",&v);
printf("%d ",v);
addnode(v,strchr(s,',')+1);//查找字符',',返回该字符的地址,建立新的子节点
}
return true;
}
bool bfs(vector<int>& ans)
{
queue<NODE*> q;
ans.clear();
q.push(root);
while(!q.empty())
{
NODE* u=q.front();q.pop();
if(!u->valhave)return false;
ans.push_back(u->val);
if(u->left!=NULL)q.push(u->left);
if(u->right!=NULL)q.push(u->right);
}
return true;
}
int main()
{
vector<int> ans;
while(readinput())
{
if(!bfs(ans))printf("no complete\n");
else
{
for(int i=0;i<ans.size();i++)
{
if(i!=0)printf(" ");
printf("%d",ans[i]);//输出最后一个字符没有空格
}
printf("\n");
}
}
return 0;
}
先序遍历:F(T)=ROOT+F(LT)+F(RT)【第一个元素为根节点】
中序遍历: F(T)=F(LT)+ROOT+F(RT) 【根据根节点构建出二叉树】
后序遍历: F(T)=F(LT)+F(RT)+ROOT 【最后一个元素为根节点】
题意:给定节点编号(权各不相同,都是正整数)的二叉树的中序和后序遍历,找一个叶子使得它到根的路径上的权和最小。如果有多解,该叶子本身的编号应尽量小
算法:递归建树(因为给定的序列满足递归条件),然后DFS(先序遍历)。
#include<cstdio>
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
using namespace std;
const int maxn=10000;
int in[maxn],post[maxn],ltree[maxn],rtree[maxn];
int n;
bool readline(int *a)
{
string line;
if(!getline(cin, line)) return false;
stringstream ss(line);
n = 0;
int x;
while(ss >> x)
a[n++] = x;
return n>0;
}
int build(int L1,int R1,int L2,int R2)//根据中序和后序递归建树
{
if(L1>R1)return 0;//递归退出条件
int root=post[R2];
int cnt=L1;
for(int i=L1;in[i]!=root;i++)
++cnt;
int lcnt=cnt-L1;//左子树节点个数
ltree[root]=build(L1,cnt-1,L2,L2+lcnt-1);//返回根节点的左子树节点编号
rtree[root]=build(cnt+1,R1,L2+lcnt,R2-1);
return root;
}
int sumbest,best;
void dfs(int root,int sum)
{
sum+=root;
if(!ltree[root]&& !rtree[root])
{
if(sum<sumbest || (sum==sumbest && root<best)){sumbest=sum;best=root;}
}
if(ltree[root])dfs(ltree[root],sum);
if(rtree[root])dfs(rtree[root],sum);
}
int main()
{
while(readline(in))
{
readline(post);
build(0,n-1,0,n-1);
sumbest=100000;
dfs(post[n-1],0);
cout<<best<<"\n";
}
return 0;
}
1.明确递归函数的功能
2.找到递归的出口
3.写下一级递归函数
4.实现递归函数的功能,该功能语句一般放在下一级递归函数之前,为下一级函数提供进入出口条件,防止无限递归
递归的过程就是建立栈帧的过程,然后从最后一个递归删除栈帧。
// UVa839题意:输入一个树状天平,根据力矩相等原则判断是否平衡。采用递归方式输入,0表示中间结点
#include<iostream>
using namespace std;
bool solve(int &w){
int wl,wr,d1,d2;
bool b1=true,b2=true;
cin>>wl>>d1>>wr>>d2;
if(!wl)b1=solve(wl);
if(!wr)b2=solve(wr);
w=wl+wr;//将下一级递归的wl+wr赋值给上一级,使上一级w不为0
return b1 && b2 && (wl*d1==wr*d2);//判断每一层递归的条件
}
int main()
{
int T,w=0;
cin>>T;
while(T--){
if(solve(w))cout<<"YES"<<endl;
else cout<<"NO"<<endl;
if(T)cout<<endl;
}
return 0;
}
题意:给一棵二叉树,每个节点都有一个水平位置:左儿子在它左边1个单位,右儿子在右边1个单位。从左向右输出每个水平位置的所有结点的权值之和。按照递归方式输入,-1表示空树
算法:在“建树”的同时计算,无须真正的把树保存下来
递归一定要注意函数的功能,出口条件,最后模拟一下递归的栈帧调用与删除找到上下级递归函数之间的关系。
//uva 699
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1000;
int sum[maxn];
void build(int p)//建立根节点位置为p的二叉树各个位置的和
{
int v;
cin>>v;
if(v==-1)return ;
sum[p]+=v;
build(p-1);build(p+1);
}
bool init()
{
int v;
cin>>v;
if(v==-1)return false;
memset(sum,0,sizeof(sum));
int p=maxn/2;
sum[p]=v;
build(p-1);build(p+1);
}
int main()
{
int kase=0;
while(init())
{
int p=0;
while(sum[p]==0)p++;//最左边的叶子位置
cout<<"case "<<++kase<<":\n"<<sum[p++];
while(sum[p]!=0)cout<<" "<<sum[p++];
cout<<endl<<endl;
}
return 0;
}
题意:给两棵四分树的先序遍历,求二者合并之后(黑色部分合并)黑色像素的个数。p 表示中间结点,f表示黑色(full),e表示白色(empty)
算法:先建树,然后统计
每一个节点建树的功能相同,所以采用递归形式,注意递归函数之间的关系。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1034;
const int len=32;
char s[maxn];
int buf[len][len],cnt;
void draw(const char *s,int& p,int r, int c, int w){
char ch=s[p++];
if(ch=='p')
{
draw(s,p,r,c+w/2,w/2);
draw(s,p,r,c,w/2);
draw(s,p,r+w/2,c,w/2);
draw(s,p,r+w/2,c+w/2,w/2);
}
else if(ch=='f'){
for(int i=r;i<r+w;i++)
for(int j=c;j<c+w;j++)
if(buf[i][j]==0){buf[i][j]=1;cnt++;}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
memset(buf,0,sizeof(buf));
cnt=0;
for(int i=0;i<2;i++)
{
scanf("%s",s);
int p=0;
draw(s,p,0,0,len);//画出节点位置为p的边长为len起始位置为(0,0)的四分树,并统计色素个数
}
cout<<"There are "<<cnt<<" black pixels."<<endl;
}
return 0;
}
题意:只要是相邻元素就有相同的编号,找矩阵元素的上下左右对角线相同元素,找到给他们编号记录。(元素的相邻元素的相邻元素也是同一个编号,适用于递归)
用DFS找矩阵的连通块:因为对每一个元素实现的功能相同,所以对元素的上下左右对角线采用递归遍历。
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=105;
char pic[maxn][maxn];
int m,n;
int idx[maxn][maxn];
void dfs(int i,int j,int id)
{
if(i<0 || i>=m || j<0 || j>=n) return;
if(idx[i][j]>0 || pic[i][j] !='@') return;
idx[i][j]=id;//更新下一级递归函数的参数,一定要在下一级递归之前更新,否则会无限递归永远也找不到出口
for(int ii=-1;ii<=1;ii++)
for(int jj=-1;jj<=1;jj++)//3*3个位置除去本身一共遍历八个
if(ii!=0 || jj!=0) dfs(i+ii,j+jj,id);
}
int main()
{
while(scanf("%d%d",&m,&n)==2 && m && n)
{
for(int i=0;i<m;i++)scanf("%s",pic[i]);
memset(idx,0,sizeof(idx));
int cnt=0;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(idx[i][j]==0 && pic[i][j]=='@')
dfs(i,j,++cnt);//给(i,j)位置所有相邻元素编号为cnt。
cout<<cnt<<endl;
}
return 0;
}
二叉树的BFS节点访问顺序是他们到根节点的距离从小到大的顺序,在图中,BFS可以假定一个起点节点,开始用BFS遍历每个节点,逐步计算每个节点到起点节点的最短距离,并且记录每个节点的前一个父节点。
没有权值,时间复杂度为O(V+E) v个顶点E条边。
// UVa816 Abbott's Revenge
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=10;
const char* dirs="NESW";//方向
const char* turns="FLR";//转弯方式
int d[maxn][maxn][4];//初始状态到位于(r,c)朝向dir的最短距离
const int dr[]={-1,0,1,0};//进入某个方向以后的行
const int dc[]={0,1,0,-1};//进入某个方向以后的列
int r0,c0,dir,r1,c1,r2,c2;//起点 终点 起点朝向
int edge[maxn][maxn][4][3];//记录输入的所有信息
struct node{
int r,c,dir;//行 列 朝向 0 1 2 3代表 N E S W
node(int r=0,int c=0,int dir=0):r(r),c(c),dir(dir){}
};
node p[maxn][maxn][4];//保存所有父节点信息
int dirid(char c)
{
return (strchr(dirs,c)-dirs);
}
int turnid(char c) { return (strchr(turns, c) - turns); }
bool read()//读取输入,保存数据
{
char s[99],s2[99],s3[99];//输入规则 保存读入的起点朝向 交叉方向规则
if(scanf("%s%d%d%s%d%d",s,&r0,&c0,s2,&r2,&c2)!=6)return false;
printf("%s\n",s);
dir=dirid(s2[0]);//将字符朝向转为数字
r1=r0+dr[dir];//初始行
c1=c0+dc[dir];//初始列
memset(edge,0,sizeof(edge));
for(;;)
{
int r,c;
scanf("%d",&r);
if (r==0)break;
scanf("%d",&c);
while(scanf("%s",s3)==1 && s3[0]!='*')
{
for(int i=1;i<strlen(s3);i++)
edge[r][c][dirid(s3[0])][turnid(s3[i])]=1;
}
}
return true;
}
node walk(const node& u,int turn)
{
int dir=u.dir;
if(turn==1)dir=(dir+3)%4;//左转以后的方向
if(turn==2)dir=(dir+1)%4;//右转以后的方向
return node(u.r+dr[dir],u.c+dc[dir],dir);
}
void print(node u)
{
vector<node> nodes;
for(;;)//根据父节点由叶子节点回溯到根节点
{
nodes.push_back(u);
if (d[u.r][u.c][u.dir]==0)break;
u=p[u.r][u.c][u.dir];
}
nodes.push_back(node(r0,c0,dir));
int cnt=0;
for(int i=nodes.size()-1;i>=0;i--)
{
if(cnt%10==0)printf(" ");
printf(" (%d,%d)",nodes[i].r,nodes[i].c);
if(++cnt%10==0)printf("\n");
}
if(nodes.size()%10!=0)printf("\n");
}
void solve()
{
queue<node> q;
node u(r1,c1,dir);//建立根节点
memset(d,-1,sizeof(d));
d[u.r][u.c][u.dir]=0;
q.push(u);
while(!q.empty())
{
node u=q.front();q.pop();
if(u.r==r2 && u.c==c2 ) {print(u);return;}
//开始遍历当前节点的每一个子节点并且标记根到该节点的值,如果已经有值就跳过
for(int i=0;i<3;i++)
{
node v=walk(u,i);
if(d[v.r][v.c][v.dir]<0 && v.r >= 1 && v.r <= 9 && v.c >= 1 && v.c <= 9 && edge[u.r][u.c][u.dir][i] )
{
d[v.r][v.c][v.dir]=d[u.r][u.c][u.dir]+1;
p[v.r][v.c][v.dir]=u;
q.push(v);
}
}
}
printf(" No Solution Possible\n");
}
int main()
{
while(read())
{
solve();
}
return 0;
}
把一个图的所有节点排序,使得每一条有向边(u,v)对应的u都排在v的前面。
如果图中存在有向环,则不存在拓扑排序,反之则存在。
// UVa10305 Ordering Tasks
#include<cstdio>
#include<cstring>
const int maxn = 1000;
int n, m, G[maxn][maxn], c[maxn], topo[maxn], t;
bool dfs(int u){
c[u] = -1;
for(int v = 0; v < n; v++) if(G[u][v]) {
if(c[v]<0) return false;
else if(!c[v]) dfs(v);
}
c[u] = 1; topo[--t]=u;
return true;
}
bool toposort(){
t = n;
memset(c, 0, sizeof(c));
for(int u = 0; u < n; u++) if(!c[u])
if(!dfs(u)) return false;
return true;
}
int main() {
while(scanf("%d%d", &n, &m) == 2 && n) {
memset(G, 0, sizeof(G));
for(int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v); u--; v--;
G[u][v] = 1;
}
if(toposort()) {
for(int i = 0; i < n-1; i++)
printf("%d ", topo[i]+1);
printf("%d\n", topo[n-1]+1);
}
else
printf("No\n");
}
}
从图中一个节点出发,图中所有边恰好经过一次,到达另一个节点。
存在条件:图必须连通(没有孤立点),无向图的度数最多只有两个奇点,如果两个奇点,则必须从一个奇点出发到达另一个奇点才有路径,如果没有奇点,从任意一个顶点出发都会回到该顶点。有向图最多只能有两个点的入度不等于出度并且出度恰好比入度大一(起点),入度比出度大一(终点)。
// UVa10129 Play on Words
// 题意:输入n个单词,是否可以排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同
// 算法:把字母看作结点,单词看成有向边,则有解当且仅当图中有欧拉路径。注意要先判连通
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1000 + 5;
int pa[256];
int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; }
int used[256], deg[256]; // 是否出现过;度数
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n;
char word[maxn];
scanf("%d", &n);
memset(used, 0, sizeof(used));
memset(deg, 0, sizeof(deg));
for(int ch = 'a'; ch <= 'z'; ch++) pa[ch] = ch; // 初始化并查集
int cc = 26; // 连通块个数
for(int i = 0; i < n; i++) {
scanf("%s", word);
char c1 = word[0], c2 = word[strlen(word)-1];
deg[c1]++;
deg[c2]--;
used[c1] = used[c2] = 1;
int s1 = findset(c1), s2 = findset(c2);
if(s1 != s2) { pa[s1] = s2; cc--; }
}
vector<int> d;
for(int ch = 'a'; ch <= 'z'; ch++) {
if(!used[ch]) cc--; // 没出现过的字母
else if(deg[ch] != 0) d.push_back(deg[ch]);
}
bool ok = false;
if(cc == 1 && (d.empty() || (d.size() == 2 && (d[0] == 1 || d[0] == -1)))) ok = true;
if(ok) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}
//UVa10562 Undraw the Trees
// 题意:把画得挺好看的多叉树转化为括号表示法
// 算法:直接在二维字符数组里递归。注意空树,并且结点标号可以是任意可打印字符
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int maxn = 200 + 10;
int n;
char buf[maxn][maxn];
// 递归遍历并且输出以字符buf[r][c]为根的树
void dfs(int r, int c) {
printf("%c(", buf[r][c]);
if(r+1 < n && buf[r+1][c] == '|') { // 有子树
int i = c;
while(i-1 >= 0 && buf[r+2][i-1] == '-') i--; // 找"----"的左边界
while(buf[r+2][i] == '-' && buf[r+3][i] != '\0') {
if(!isspace(buf[r+3][i])) dfs(r+3, i); // fgets读入的'\n'也满足isspace()
i++;
}
}
printf(")");
}
void solve() {
n = 0;
for(;;) {
fgets(buf[n], maxn, stdin);
if(buf[n][0] == '#') break; else n++;
}
printf("(");
if(n) {
for(int i = 0; i < strlen(buf[0]); i++)
if(buf[0][i] != ' ') { dfs(0, i); break; }
}
printf(")\n");
}
int main() {
int T;
fgets(buf[0], maxn, stdin);
sscanf(buf[0], "%d", &T);
while(T--) solve();
return 0;
}
可以用一个vector数组来保存G[i],代表i节点连接的另一个节点。
// UVa506 System Dependencies
// 注意:显式安装的必须显式删除
#include<iostream>
#include<cstring>
#include<string>
#include<sstream>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 10000;
int cnt = 0;
map<string, int> name2id;
string name[maxn];
vector<int> depend[maxn], depend2[maxn];
int status[maxn]; // 0-not installed, 1-explicitly installed, 2-implicitly installed
vector<int> installed;
int ID(const string& item) {
if(!name2id.count(item)) {
name[++cnt] = item;
name2id[item] = cnt;
}
return name2id[item];
}
bool needed(int item) {
for(int i = 0; i < depend2[item].size(); i++)
if(status[depend2[item][i]]) return true;
return false;
}
void install(int item, bool toplevel) {
if(!status[item]) {
for(int i = 0; i < depend[item].size(); i++)
install(depend[item][i], false);
cout << " Installing " << name[item] << "\n";
status[item] = toplevel ? 1 : 2;
installed.push_back(item);
}
}
void remove(int item, bool toplevel) {
if((toplevel || status[item] == 2) && !needed(item)) {
status[item] = 0;
installed.erase(remove(installed.begin(), installed.end(), item), installed.end());
cout << " Removing " << name[item] << "\n";
for(int i = 0; i < depend[item].size(); i++)
remove(depend[item][i], false);
}
}
// 按照安装顺序输出
void list() {
for(int i = 0; i < installed.size(); i++)
cout << " " << name[installed[i]] << "\n";
}
int main() {
string line, cmd;
memset(status, 0, sizeof(status));
while(getline(cin, line)) {
cout << line << "\n";
stringstream ss(line);
ss >> cmd;
if(cmd[0] == 'E') break;
string item1, item2;
if(cmd[0] == 'L') list();
else {
ss >> item1;
int i1 = ID(item1);
if(cmd[0] == 'D') {
while(ss >> item2) {
int i2 = ID(item2);
depend[i1].push_back(i2);
depend2[i2].push_back(i1);
}
}
else if(cmd[0] == 'I') {
if(status[i1]) cout << " " << item1 << " is already installed.\n";
else install(i1, true);
}
else {
if(!status[i1]) cout << " " << item1 << " is not installed.\n";
else if(needed(i1)) cout << " " << item1 << " is still needed.\n";
else remove(i1, true);
}
}
}
return 0;
}