3.17-3.19总结

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

思路:

  1. dfs+栈,输出路线
  2. 一个二维数组 int city[25][3] 表示i相邻的3个城市
  3. 题目要求字典序输出,可以给city中相邻的城市排序,但好像题目数据已经排好了
  4. dfs搜索时入栈并标记,回溯时出栈消除标记
  5. 城市从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

思路:

  1. 在没有传送点时,与一维bfs搜索一样,入队和出队
  2. 当走到传送点时,如果在第一(二)层就将第二(一)层中相应的位置入队,同时标记传送点和落脚点。
  3. 当传送过去还是传送点时,回陷入死循环,所以要判断落脚点是不是传送点
  4. 有可能传送过去就是终点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

  1. 数组元素个数n如果为偶数,0的个数小于等于n/2,输出0
  2. n如果为奇数,0小于等于n/2+1,输出0
  3. 这是因为上面两种情况,可以将0插在数与数之间,构造出来的新数组可以不含0
  4. 如果上面两种情况都不满足,就看1的个数。
  5. 如果除了0剩下的全是1(1要存在),就输出2,因为构造出来的新数组必定含0和1
  6. 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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值