较难题: E , J E,J E,J
防 A K AK AK 题: I I I
其余题目均为一般可做题
给定一个字符串,连续相邻3个字符相等的可以消去,重复此操作,要求输出操作次数和最后剩下的字符串。
栈的原理(类似于括号配对问题),数组模拟,把字符依次放入栈,当栈的大小大于等于3的时候随后放进来的跟前面两个比较相等弹出栈并记录答案,最后输出栈里还剩的字符即可(特判栈为空的情况)。
#include
#include
const int N = 1e5+5;
char st_Q[N],s[N];
int main() {
while(~scanf("%s",s)) {
int ans=0,top=0;
int len =strlen(s);
for(int i=0; i<len; i++) {
st_Q[top++]=s[i];//入栈
if(top>=3&&st_Q[top-1]==st_Q[top-2]&&st_Q[top-2]==st_Q[top-3]) {
top-=3;//相当于出栈
ans++;
}
}
printf("%d ",ans);
if(top) {
for(int i=0; i<top; i++)printf("%c",st_Q[i]);
} else {
printf("qwq");
}
printf("\n");
}
return 0;
}
给定种类和对应数量的糖果,现在不能两次吃同一种糖果,问你是否存在一种吃糖果的顺序把所有糖果都吃完。
高中数学组合原理,插空法:考虑最大数量的那种糖果,如果剩下的糖果能插在这些数量中间即可,否则不行。(注意和会暴int,虽然不影响结果)
#include
#define max(a,b) a>b?a:b
int main() {
int t;
while(~scanf("%d",&t)) {
while(t--) {
int n,maxn=0;
long long sum=0;
scanf("%d",&n);
while(n--) {
int x;
scanf("%d",&x);
maxn=max(x,maxn);
sum+=x;
}
if(sum-maxn>=maxn-1) {//剩下的数量为sum-maxn,最大数量的这种糖果有maxn-1个空
printf("Yes\n");
} else {
printf("No\n");
}
}
}
return 0;
}
给定 7 ∗ 21 7*21 7∗21的字符图案,打印读数结果。
这类题,观察不同类型数字的特征为最好办法。(特征有很多,代码仅供参考)
#include
#include
using namespace std;
char a[7][21];
int solve(int id) {
if (a[0][id + 1] == 'X') {
if (a[1][id] == 'X') {
if (a[1][id + 3] == 'X') {
if (a[3][id + 1] == 'X') {
if (a[4][id] == 'X')
return 8;
else
return 9;
} else {
return 0;
}
} else {
if (a[4][id] == 'X')
return 6;
else
return 5;
}
} else {
if (a[4][id] == 'X')
return 2;
else if (a[3][id+1] == 'X')
return 3;
else
return 7;
}
} else {
if (a[1][id] == 'X')
return 4;
else
return 1;
}
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
for(int i=0; i<7; i++) {
scanf("%s",a[i]);
}
int a,b,c,d;
a=solve(0);//这里用到solve(id)函数 ,表示从id列开始的图形区域,来区分四个数字
b=solve(5);
c=solve(12);
d=solve(17);
printf("%d%d:%d%d\n",a,b,c,d);
}
return 0;
}
给定一个数字串,求能组成多少个由 8 8 8开头的 11 11 11位电话号码。(每一个位置的数字只能用一次,组成的电话号码可以一样)
输入字符串后经过计算 k k k:数字8的个数 , l e n len len:数字的总个数,那么: 如果 k < l e n 11 k<\frac{len}{11} k<11len最多组成 k k k个电话号码,否则最多组成 l e n 11 \frac{len}{11} 11len个号码,所以答案是 m i n ( k , l e n 11 ) min(k,\frac{len}{11}) min(k,11len)。
#include
#include
#define min(a,b) a
const int N = 2005;
char s[N];
int main() {
int len;
int k;//记录字符8出现的次数
while(~scanf("%d",&len)) {
k=0;
scanf("%s",s);
for(int i=0; i<len; ++i) if(s[i]=='8') k++;
int ans=min(k,len/11);
printf("%d\n",ans);
}
return 0;
}
给定 n n n个三元组,还原原序列,如果原序列不唯一,输出字典序最小的序列。
思路1:
d f s dfs dfs搜索:本题会发现在 n ≤ 10 n≤10 n≤10的时候可以进行 d f s dfs dfs的全排列搜索,初设数列设为 1 − n 1-n 1−n,去判断此时数列的所有三元组是否与输入的完全一致,如果一致,输出当前数列,不一致时进行字典序的升序操作,再进行判断,一定会有一个数列满足输入情况。
思路2:
当一个数列的所有三元组给出后,可以发现,数列的第一个位置和最后一个位置,一定只出现了一次,第二个位置和倒数第二个位置出现了两次,其余位置出现了三次,可以先进行桶排计次后,寻找1-n中第一个只出现一次的数,将那个数元组中所有数的计次减一,同一元组中下一个次数为一的位置即为下一个数
如:元组为(1,4,2),(4,2,3),(2,3,5)时
1共计一次,2三次,3两次,4两次,5一次
所以首位置为1,将第一个元组中所有计次减一,4的计次变为1,2的计次变为2;
得到第二个位置的数是4,第三个位置的数是2,然后寻找与本元组仅一个数不同的元组,可得到下一个数为3,然后可得到再下一个数为5
所以数列为1 4 2 3 5
#include
#include
#include
using namespace std;
vector<int> g[15];
int vis[15];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n-2; i++) {
int x, y, z;
cin >> x >> y >> z;
vis[x]++;
vis[y]++;
vis[z]++;
g[x].push_back(y);
g[x].push_back(z);
g[y].push_back(x);
g[y].push_back(z);
g[z].push_back(x);
g[z].push_back(y);
}
if(n == 3) { //对于n = 3 和 4 的时候,元组中不存在计次为3的数,进行直接判断输出即可
printf("1 2 3\n");
return 0;
} else if(n == 4) {
int t;
for (int i = 1; i <= n; i++) {
if (vis[i] == 1) {
t = i;
printf("%d ",i);
break;
}
}
for (int i = 1; i <= n; i++) {
if (vis[i] == 2)
printf("%d ",i);
}
for (int i = n; i >= 1; i--) {
if (vis[i] == 1) {
t = i;
printf("%d ",i);
break;
}
}
return 0;
}
int s1 = 0, s2 = 0;
for (int i = 1; i <= n; i++) { //找到第一个位置的数
if (vis[i] == 1) {
s1 = i;
break;
}
}
for (int i = 1; i <= n; i++) { //找到第二个位置的数
if (vis[i] == 2) {
int ok = 0;
for (int to : g[s1]) {
if (to == i)
ok = 1;
if (ok)
s2 = i;
}
}
}
vis[s1] = vis[s2] = -1; //标记为-1代表用过这个数了
printf("%d %d", s1, s2);
for (int i = 3; i <= n; i++) { //由于三元组可知,已知两个位置可寻找下一位置,从第三个位置开始依次寻找下一个位置的数
int nt = 0;
for (int to : g[s1]) {
if (vis[to] != -1)
nt = to;
}
s1 = s2;
s2 = nt;
vis[nt] = -1;
printf(" %d", nt);
}
return 0;
}
ps:看不懂的问出题人 w b t : q q : 757481375 wbt:qq:757481375 wbt:qq:757481375,或者学习别人的暴力代码。
输入一个数,根据其模4所得数字大小来判定等级。
此题是一道数学题,为了在限定范围内达到最高等级,先求出n被4除的余数,然后根据余数判断即可。
#include
int main() {
int n;
scanf("%d", &n);
n %= 4;
if (n == 1) {
printf("0 A");
} else if (n == 2) {
printf("1 B");
} else if (n == 3) {
printf("2 A");
} else {
printf("1 A");
}
return 0;
}
题数大的为冠军,比较字符串即可。
比较字符串长度即可,字符串长度相同时,比较字典序。
#include
#include
#include
#include
using namespace std;
struct node{
int num;
string a;
}st[25];
int cmp(node xx,node yy){
if(xx.a.length()!=yy.a.length()){
return xx.a.length()<yy.a.length();
}else{
return xx.a<yy.a;
}
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
st[i].num=i+1;
cin>>st[i].a;
}
sort(st,st+n,cmp);
cout<<st[n-1].num<<endl<<st[n-1].a;
return 0;
}
#include
#include
#include
using namespace std;
char ans[105],b[105];
int main() {
int n,len,max=0,num;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%s",b);
len=strlen(b);
if(len>max) {
max=len;
num=i;
strcpy(ans,b);
} else if(len==max) {
if(strcmp(ans,b)<0) {
num=i;
strcpy(ans,b);
}
}
}
printf("%d\n%s\n",num,ans);
return 0;
}
判断给定的字符串是否幸运:如果字符串包含的所有字母都是连续(相邻)的字母,并且每个字母恰好出现一次,则该字符串称为幸运的字符串。
桶记录字母出现次数, m i n n minn minn记录最小字母, m a x x maxx maxx记录最大字母,然后从 m i n n minn minn到 m a x x maxx maxx遍历桶,存在非1就不幸运,否则幸运。(当然sort再判断也是可以的)。
#include
#include
#define min(a,b) a
#define max(a,b) a>b?a:b
#define N 1005
int ans[N];
char s[N];
int main() {
int n;
scanf("%d",&n);
while(n--) {
memset(ans,0,sizeof(ans));
scanf("%s",s);
int maxx = 0,minn ='z'-'a';
int len =strlen(s);
for(int i=0; i<len; i++) {
ans[s[i]-'a']++;
maxx = max(maxx,s[i]-'a');
minn = min(minn,s[i]-'a');
}
bool flag =0;
for(int i=minn; i<=maxx; i++) {
if(ans[i] != 1) {
flag =1;
break;
}
}
printf("%s\n",flag?"Unlucky":"Lucky");
}
return 0;
}
给定一个 n ∗ m n*m n∗m迷宫单元,有一个无限区域由这个迷宫单元平铺,给定一个起点,问能否无限走下去。
事实上,只要从一个地图块上的某一点 ( x , y ) (x,\ y) (x, y) 出发走到其他地图块上的同一点 ( x , y ) (x,\ y) (x, y) 处,即可无限前进。把这一点想清楚以后问题就解决了。
注意,上述的点 ( x , y ) (x,\ y) (x, y) 不一定必须是边界点。
至于如何记录是从不同地图到达的,需要一些小技巧。我们在 B F S BFS BFS时维护无限大地图上的绝对坐标 ( a x , a y ) (ax,\ ay) (ax, ay) ,另外维护一个数组 f r o m [ r x ] [ r y ] from[rx][ry] from[rx][ry] 表示地图块上的某一相对坐标 ( r x , r y ) (rx,\ ry) (rx, ry) 所对应的绝对坐标,如果出现 f r o m [ r x ] [ r y ] from[rx][ry] from[rx][ry] 已有记录、且与当前 ( a x , a y ) (ax,\ ay) (ax, ay) 不同的情况,则说明我们到达了不同地图块上的同一点。
ps:在 n u o y a n l i O j nuoyanliOj nuoyanliOj(栈深度1e7)本题用DFS也可以AC,那就是因为 OJ 上的栈空间很大(比如 N S W O J 2.0 NSWOJ2.0 NSWOJ2.0或者 D o m j u d g e Domjudge Domjudge),在通常的OJ或者 PC^2 上递归这么多层早就爆掉了。因此建议用 B F S BFS BFS求解。
#include
#include
#include
using namespace std;
struct Point {
int x, y;
Point(int i, int j) : x(i), y(j) {}
Point() {}
};
const int dx[] = { 1, -1, 0, 0 };
const int dy[] = { 0, 0, 1, -1 };
const int MAX = 2005;
char maze[MAX][MAX];
Point from[MAX][MAX];
int n, m;
bool bfs(Point st) {
queue<Point> q;
q.push(st);
while (!q.empty()) {
Point p = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int ax = p.x + dx[i], ay = p.y + dy[i];
int rx = ax, ry = ay;
rx = ((rx % n) + n) % n;//如果超过n*m边界就到下一个单元的对应位置
ry = ((ry % m) + m) % m;
if (maze[rx][ry] == '#') {
continue;
}
if (from[rx][ry].x == -1&&from[rx][ry].y==-1) {
from[rx][ry] = Point(ax, ay);
q.push(Point(ax, ay));
} else if (from[rx][ry].x != ax||from[rx][ry].y != ay) {
return true;
}
}
}
return false;
}
int main() {
// freopen("test.txt", "r", stdin);
while (scanf("%d %d", &n, &m) != EOF) {
memset(from, -1, sizeof(from));
for (int i = 0; i < n; i++) {
scanf("%s", maze[i]);
}
Point st;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
if (maze[i][j] == 'S') {
st = Point(i, j);
}
}
bool flag = bfs(st);
if (flag) {
printf("Yes\n");
} else {
printf("No\n");
}
}
return 0;
}
能走到起点无限远就意味着可以从起始点沿着某个方向一直走,而不会走回头路回到起点。(进入循环)因此我们dfs要用到4个参数: x , y , f x , f y x,y,fx,fy x,y,fx,fy,其中 x 、 y x、y x、y代表此时在地图上的位置,也即当 x < 0 ∣ ∣ x > = n ∣ ∣ y < 0 ∣ ∣ y > = m x<0||x>=n||y<0||y>=m x<0∣∣x>=n∣∣y<0∣∣y>=m的时候是要对 x x x和 y y y进行单元格操作的,但是 f x fx fx和 f y fy fy代表当前位置相对初始位置的移动,无需做额外操作,那么当 f x fx fx== x x x&&$ f y = = y fy==y fy==y且该点被访问过的时候,说明这是一个死循环,但不是解。因为沿着某个方向若能回到起点, 必定是满足 f x ! = x ∣ ∣ f y ! = y fx!=x||fy!=y fx!=x∣∣fy!=y的。 那么我们考虑用一个三维 v i s vis vis数组,每次 d f s dfs dfs令: v i s [ x ] [ y ] [ 1 ] = f x , v i s [ x ] [ y ] [ 2 ] = f y , v i s [ x ] [ y ] [ 0 ] = 1 vis[x][y][1]=fx,vis[x][y][2]=fy,vis[x][y][0]=1 vis[x][y][1]=fx,vis[x][y][2]=fy,vis[x][y][0]=1,那么当 v i s [ x ] [ y ] [ 0 ] vis[x][y][0] vis[x][y][0]&& ( v i s [ x ] [ y ] [ 1 ] ! = x ∣ ∣ v i s [ x ] [ y ] [ 2 ] ! = y ) (vis[x][y][1]!=x||vis[x][y][2]!=y) (vis[x][y][1]!=x∣∣vis[x][y][2]!=y)的时候,说明我们找到了一个满足题意的解。即经过有效的移动又走到了以前走过的点,那么必定可以沿着这条路径一直走下去。若 v i s [ x ] [ y ] [ 0 ] = 1 vis[x][y][0]=1 vis[x][y][0]=1但是不满足上式,说明已经走过。
#include
#include
#include
using namespace std;
int vis[2005][2005][3];//:vis[x][y][1]=fx,vis[x][y][2]=fy,vis[x][y][0]=1,那么当vis[x][y][0]&&(vis[x][y][1]!=x||vis[x][y][2]!=y)的时候,说明我们找到了一个满足题意的解
char s[2005][2005];
int n,m;
bool flag;
const int dir[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y,int fx,int fy) {
if(flag) {
return;
}
if(vis[x][y][0]&&(vis[x][y][1]!=fx||vis[x][y][2]!=fy)) { //重复走过的点
flag=true;
return;
} else if(vis[x][y][0]) {
return ;
}
vis[x][y][0]=1;
vis[x][y][1]=fx;
vis[x][y][2]=fy;
for(int i=0; i<4; i++) {
int dx=(x+dir[i][0]+n)%n;
int dy=(y+dir[i][1]+m)%m;
if(s[dx][dy]!='#')
dfs(dx,dy,fx+dir[i][0],fy+dir[i][1]);
}
}
int main() {
while(~scanf("%d %d",&n,&m)) {
flag=0;
memset(vis,0,sizeof(vis));
int x=0,y=0;
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
cin>>s[i][j];
if(s[i][j]=='S')
x=i,y=j;
}
}
if(n==1&&m==1) {
printf("Yes\n");
continue;
}
dfs(x,y,x,y);
printf("%s\n",flag?"Yes":"No");
}
return 0;
}
求组合数 C ( m n ) C\binom{m}{n} C(nm),其中: C ( m n ) = n ! m ! ∗ ( n − m ) ! C\binom{m}{n}=\frac{n!}{m!*(n-m)!} C(nm)=m!∗(n−m)!n!, 1 ≤ m ≤ n ≤ 61 1 \leq m \leq n \leq 61 1≤m≤n≤61。
不难发现单纯暴力求解会导致中间结果溢出(不展开公式你就错了\[斜眼笑]),所以一定要在计算过程中约分。
比如
C ( 4 9 ) = 6 ∗ 7 ∗ 8 ∗ 9 1 ∗ 2 ∗ 3 ∗ 4 C\binom{4}{9}=\frac{6*7*8*9}{1*2*3*4} C(94)=1∗2∗3∗46∗7∗8∗9,先计算 6 / 1 6/1 6/1再乘上 7 / 2 7/2 7/2再乘上 8 / 3 8/3 8/3再乘上 9 / 4 9/4 9/4,
而
6 / 1 = C ( 1 6 ) 6/1=C\binom{1}{6} 6/1=C(61)
( 6 ∗ 7 ) / ( 1 ∗ 2 ) = C ( 2 7 ) (6*7)/(1*2)=C\binom{2}{7} (6∗7)/(1∗2)=C(72)
( 6 ∗ 7 ∗ 8 ) / ( 1 ∗ 2 ∗ 3 ) = C ( 3 8 ) (6*7*8)/(1*2*3)=C\binom{3}{8} (6∗7∗8)/(1∗2∗3)=C(83)
… 一定都是整数,即都能整除
#include
typedef long long ll;
ll C(ll n,ll m) {
ll ans=1;
for(ll i=1; i<=m; i++) {
ans=ans*(n-m+i)/i;
}
return ans;
}
int main() {
int n,m;
while(~scanf("%d%d",&n,&m)) {
printf("%lld\n",C(n,m));
}
return 0;
}
当然这里还有一个可以小优化的(在这里优化与否无所谓): C ( m n ) = C ( n − m n ) C\binom{m}{n}=C\binom{n-m}{n} C(nm)=C(nn−m)
#include
typedef long long ll;
ll C(ll n,ll m) {
ll ans=1;
for(ll i=1; i<=m; i++) {
ans=ans*(n-m+i)/i;
}
return ans;
}
int main() {
int n,m;
while(~scanf("%d%d",&n,&m)) {
if(n-m<m)m=n-m;
printf("%lld\n",C(n,m));
}
return 0;
}
还有这个公式: C ( m n ) = C ( m − 1 n − 1 ) + C ( m n − 1 ) C\binom{m}{n}=C\binom{m-1}{n-1}+C\binom{m}{n-1} C(nm)=C(n−1m−1)+C(n−1m)(杨辉三角),那么就存在另外一种递推预处理:
参考代码:
#include
typedef long long ll;
const int N=62;
ll ans[N][N];
void init_C() {
for(int i=0; i<N; i++) {
ans[i][0]=ans[i][i]=1;//初始化边界
}
//回想一下杨辉三角理解
for(int i=2; i<N; i++) { //00 11 10 都算出来
for(int j=0; j<=i/2; j++) {
ans[i][j]=ans[i-1][j]+ans[i-1][j-1];
ans[i][i-j]=ans[i][j];//另外半部分直接算出来
}
}
}
int main() {
int n,m;
init_C();//一次求出所有结果
while(~scanf("%d%d",&n,&m)) {
printf("%lld\n",ans[n][m]);//输出即可
}
return 0;
}
比赛的时候发现很多同学因为数组越界或者没有初始化错误(比如H题),下次就需要注意一点了,因为有时候你的编译器不会报错误,即便是越界或者没有初始化的时候自己运行也是对的,而交上去就错,这个时候需要做的就是定义全局变量/注意初始化