B - Fliptile https://vjudge.net/contest/547627#problem/B
思路:开关问题。对于给定的网格,假设不能直接对第一排继续翻转,那么就只能通过第二排间接翻转第一排,第一排的某个位置为黑色,就翻转它下面那个格子。这样就可以让第一排全为白色。下面几行同理,对于某一行,只能通过翻转下一行来让该行全为白。再回到可以直接翻转第一排的情况,首先,我们只翻转第一排,通过枚举第一行所有翻转情况2^m种,就可以确定后续所有的翻转情况。
代码实现:
#include <iostream>
#include<string.h>
int m,n;
int mapp[20][20];
int ti[20][20];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int turn[20][20];
int tem_turn[20][20];
int ans=9999999;
void change(int map[20][20],int i,int j)//翻转该位置
{
if(map[i][j]==1)
{
map[i][j]=0;
}
else
{
map[i][j]=1;
}
}
void surrounding(int map[20][20],int i,int j)//被牵连翻转
{
int tx,ty;
for(int k=0;k<4;k++)
{
tx=i+dx[k];
ty=j+dy[k];
if(tx>=0&&tx<m&&ty>=0&&ty<n)
{
change(map,tx,ty);
}
}
}
bool judge(int temp[20][20])//判断是否全为白
{
for(int i=0;i<n;i++)
{
if(temp[m-1][i]==1)
{
return false;
}
}
return true;
}
void copy(int arr1[20][20],int arr2[20][20])//复制数组
{
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
arr1[i][j]=arr2[i][j];
}
}
}
void reset()//重置tem_turn中除第一行的翻转次数
{
for(int i=1;i<m;i++)
{
for(int j=0;j<n;j++)
{
tem_turn[i][j]=0;
}
}
}
void dfs(int x,int t)
{
if(x>n) return;
int temp[20][20];
copy(temp,mapp);
int tim=t;
for(int i=1;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(temp[i-1][j]==1)
{
change(temp,i,j);
surrounding(temp,i,j);
tem_turn[i][j]=1;
tim++;
}
}
}
if(judge(temp))
{
if(tim<ans)
{
ans=tim;
copy(turn,tem_turn);
}
}
reset();
dfs(x+1,t);//不翻转
change(mapp,0,x);
surrounding(mapp,0,x);
tem_turn[0][x]=1;
dfs(x+1,t+1);//翻转
change(mapp,0,x);
surrounding(mapp,0,x);
tem_turn[0][x]=0;
}
void output()
{
if(ans==9999999)
{
printf("IMPOSSIBLE\n");
return;
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
printf("%d ",turn[i][j]);
}
printf("\n");
}
}
int main()
{
scanf("%d%d",&m,&n);
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
scanf("%d",&mapp[i][j]);
}
}
dfs(0,0);
output();
return 0;
}
J - 哈密顿绕行世界问题 https://vjudge.net/contest/547627#problem/J
思路:
- dfs+栈,输出路线
- 一个二维数组 int city[25][3] 表示i相邻的3个城市
- 题目要求字典序输出,可以给city中相邻的城市排序,但好像题目数据已经排好了
- dfs搜索时入栈并标记,回溯时出栈消除标记
- 城市从m出发,搜索到第20个城市时,要判断能不能回到m。
代码实现
#include <string.h>
#include <stdio.h>
#include <iostream>
int city[25][3];
int m;
int book[25];
int stack[25];
int top;
int cnt;
void dfs(int n,int num)
{
if(num==20&&(city[n][0]==m||city[n][1]==m||city[n][2]==m))//城市满20且可以回到m
{
printf("%d: ",++cnt);
for(int i=1;i<=top;i++)
{
printf("%d ",stack[i]);
}
printf("%d\n",m);
}
for(int i=0;i<3;i++)
{
if(book[city[n][i]]==0)
{
book[city[n][i]]=1;
stack[++top]=city[n][i];//入栈
dfs(city[n][i],num+1);
book[city[n][i]]=0;//回溯
top--;//出栈
}
}
}
int main()
{
for(int i=1;i<=20;i++)
{
scanf("%d%d%d",&city[i][0],&city[i][1],&city[i][2]);//与i相邻的3个城市
}
while(1)
{
scanf("%d",&m);
if(m==0) break;
cnt=0;
book[m]=1;
stack[++top]=m;//首先让m入栈
dfs(m,1);
top--;
book[m]=0;
}
return 0;
}
M - A计划 https://vjudge.net/contest/547627#problem/M
思路:
- 在没有传送点时,与一维bfs搜索一样,入队和出队
- 当走到传送点时,如果在第一(二)层就将第二(一)层中相应的位置入队,同时标记传送点和落脚点。
- 当传送过去还是传送点时,回陷入死循环,所以要判断落脚点是不是传送点
- 有可能传送过去就是终点p。
代码实现
#include <string.h>
#include <stdio.h>
#include <iostream>
char mapp[2][15][15];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int n,m,t;
struct point{
int z;
int x;
int y;
int ti;
}que[500];
bool bfs(int book[2][15][15])
{
int head=0;
int tail=0;
que[head].z=0;
que[head].x=0;
que[head].y=0;
que[head].ti=0;
book[0][0][0]=1;
int nx,ny,nz,nt;
int tx,ty;
while(head<=tail)
{
nx=que[head].x;
ny=que[head].y;
nz=que[head].z;
nt=que[head].ti;
if(mapp[nz][nx][ny]=='P')
{
if(nt<=t) return true;
else return false;
}
for(int i=0;i<4;i++)
{
tx=nx+dx[i];
ty=ny+dy[i];
if(tx>=0&&ty>=0&&tx<n&&ty<m)
{
if(mapp[nz][tx][ty]!='*'&&book[nz][tx][ty]==0)
{
book[nz][tx][ty]=1;
if(mapp[nz][tx][ty]=='#')//传送
{
if(nz==0&&mapp[1][tx][ty]!='*'&&mapp[1][tx][ty]!='#'&&book[1][tx][ty]==0)//传送到的位置非墙且未标记,同时对面也不是传送门
{
que[++tail].x=tx;
que[tail].y=ty;
que[tail].z=1;
que[tail].ti=nt+1;
book[1][tx][ty]=1;
}
else if(nz==1&&mapp[0][tx][ty]!='*'&&mapp[0][tx][ty]!='#'&&book[0][tx][ty]==0)
{
que[++tail].x=tx;
que[tail].y=ty;
que[tail].ti=nt+1;
que[tail].z=0;
book[0][tx][ty]=1;
}
}
else//不是传送门
{
que[++tail].x=tx;
que[tail].y=ty;
que[tail].z=nz;
que[tail].ti=nt+1;
}
}
}
}
head++;
}
return false;
}
int main()
{
int c;
scanf("%d",&c);
getchar();
while(c--)
{
scanf("%d%d%d",&n,&m,&t);
getchar();
int book[2][15][15]={0};
for(int i=0;i<n;i++)
{
scanf("%s",mapp[0][i]);
getchar();
}
getchar();
for(int i=0;i<n;i++)
{
scanf("%s",mapp[1][i]);
getchar();
}
if(bfs(book))
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
return 0;
}
A. Walking Master https://codeforces.com/contest/1806/problem/A
思路:一句话,从起点出发,可以达到 以起点为顶点,射向x负半为其中一条边,顺时针135*的所有区域。
代码实现
#include<stdio.h>
#include<math.h>
int main()
{
int a,b,c,d;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if(d<b||c>a+d-b)
{
printf("-1\n");
continue;
}
printf("%d\n",d-b+abs(a+d-b-c));
}
return 0;
}
B. Mex Master Problem - B - Codeforces
思路: 切入点 0的数量 答案只有012
- 数组元素个数n如果为偶数,0的个数小于等于n/2,输出0
- n如果为奇数,0小于等于n/2+1,输出0
- 这是因为上面两种情况,可以将0插在数与数之间,构造出来的新数组可以不含0
- 如果上面两种情况都不满足,就看1的个数。
- 如果除了0剩下的全是1(1要存在),就输出2,因为构造出来的新数组必定含0和1
- else 输出2
代码
#include<stdio.h>
int a[200000+5];
int b[200000+5];
int main()
{
int t=0;
scanf("%d",&t);
int n;
while(t--)
{
scanf("%d",&n);
int num=0;
int num1=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==0) num++;//0的个数
if(a[i]==1) num1++;//1的个数
}
if(num<=n/2)
{
printf("0\n");
continue;
}
else if(n%2==1&&num<=n/2+1)
{
printf("0\n");
continue;
}
else
{
if(num1&&num+num1==n)
{
printf("2\n");
}
else
{
printf("1\n");
}
}
}
return 0;
}