2021.1.3搜索测试

【杂言】:

寡人怎么也没想到 T1写挂了,

\(T1\)】:

【题目描述】:

给出一个n个未知数的方程,\(x_1,x_2,x_3......x_n\)\(x_1+x_2+x_3....+x_n==S\)的正整数解的个数,并且要保证,对于任意\(i (1<=i< n)\) \(x_i\)\(x_{i+1}\)相差不大于\(p\)

【数据范围】:

对于 \(30%\) 数据 \(2<=n<=10 ,p=0,S<=30;\)对于 \(100%\) 数据 \(2<=n<=10,p<=3 , S<=30;\)保证数据有梯度。

【考场代码】:(只有三十分,合着我暴力挂了):

/*
 by : Zmonarch 
 知识点 :搜索 
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <cstring>
#define int long long
using namespace std ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ; 
}
int n , tot , s , p ;  
void search(int now , int last ,int rest)
{
	//printf("sum : %d\n" ,rest) ;
	if(now == n + 1 ) //现在已经加到了最后一个数 
	{
		if (rest == 0) tot ++ ;//方案数++
		return ;
	}
	for(int i = 1 ; i < s && abs(i - last ) <= p; i++)//前一个数和后一个数不能超过p  
	{
		search(now + 1 , i , rest - last - i) ;
	}
}
signed main()
{
//	freopen("math.in" , "r" , stdin) ;
//	freopen("math.out" , "w" , stdout) ;
	n = read() , s = read() , p = read() ;
	//for(int i = 1 ; i <= s ; i++)
	if(p == 0) //因为是正整数, 所以如果相差为零, 那么也就只有一种情况, 那就是全部都是同一个数的时候,除不尽就不是正整数了 
	{ //这里的相差是绝对值, 特判一下 , 
		if(s % n == 0) printf("1") ;
		else printf("0") ; 
	} 
	else 
	{
		search( 0  , 0 ,s) ;	
		printf("%lld", tot) ;
	}
	return 0 ;
}

挂掉的原因是 : 1. 在进行下一次搜索的时候,忘记了这次枚举的数值, 与上一次的值相差不能超过\(p\);
2. 在初始化的时候 , 对于任意的满足条件的数值,都可以作为相加的开始值

【Std】:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <cstring>
#define int long long
using namespace std ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ; 
}
int n , tot , s , p ;  
void search(int now , int last ,int rest)
{
	if(now == n) //现在已经加到了最后一个数 
	{
		if (abs(rest - last) <= p)  tot ++ ;
		return ;
	}
	for(int i = max((long long)1 , last - p) ; i <= min(last + p , rest - n + i); i++)//前一个数和后一个数不能超过p  
	{
		search(now + 1 , i , rest - i) ;
	}
}
signed main()
{
	//freopen("math.in" , "r" , stdin) ;
	//freopen("math.out" , "w" , stdout) ;
	n = read() , s = read() , p = read() ;
	//for(int i = 1 ; i <= s ; i++)
	if(p == 0) //因为是正整数, 所以如果相差为零, 那么也就只有一种情况, 那就是全部都是同一个数的时候,除不尽就不是正整数了 
	{ //这里的相差是绝对值, 特判一下 , 
		if(s % n == 0) printf("1") ;
		else printf("0") ; 
	} 
	else 
	{
		for(int i = 1 ; i <= s-(n-1) ; i++)
		search( 2 , i ,s - i) ;	
		printf("%lld", tot) ;
	}
	return 0 ;
}

【改完代码】:

/*
 by : Zmonarch 
 知识点 :搜索 
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <cstring>
#define int long long
using namespace std ;
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ; 
}
int n , tot , s , p ;  
void search(int now , int last ,int rest)
{
	//printf("sum : %d\n" ,rest) ;
	if(now == n) //现在已经加到了最后一个数 
	{
		if (rest == 0) tot ++ ;//方案数++
		return ;
	}
	for(int i = max( (long long)1 , last - p ) ; i <= s && abs(i - last ) <= p; i ++)//前一个数和后一个数不能超过p  
	{
		search(now + 1 , i , rest - i) ;
	}
}
signed main()
{
	n = read() , s = read() , p = read() ;
	//for(int i = 1 ; i <= s ; i++)
	if(p == 0) //因为是正整数, 所以如果相差为零, 那么也就只有一种情况, 那就是全部都是同一个数的时候,除不尽就不是正整数了 
	{ //这里的相差是绝对值, 特判一下 , 
		if(s % n == 0) printf("1") ;
		else printf("0") ; 
	} 
	else 
	{
		for(int i = 1 ; i <= s ; i++) //开始的值的时候也挂了 
		{		
			search( 1  , i , s - i) ;	
		}
		printf("%lld", tot) ;
	}
	return 0 ;
}

【T2】:

【题目描述】:

给出一个\(n \times n\)的网格,有一些格子是障碍,再给出一对起点终点,求从起点到终点需要的最小步数,每次可以从一个格子走到上下左右4相邻的四个格子里.

【输入】:

第一行一个整数\(n\)
以下\(n\)\(n\)个整数,描述整个网格,其中\(0\)表示没有障碍,\(1\)表示有障碍。
最后一行四个整数,\(Sx,Sy,Tx,Ty\),描述起点和终点。

【输出】 :

输出最少步数。
如果永远走不到输出-1。

【数据范围】:

对于30%的数据,\(n<=10\)
对于 100%的测试数据,\(n<=1000\)

【考场代码】:

大水题 ,一眼切 , 直接过。

/*
 by : Zmonarch
 知识点 : 搜索 
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <cstring>
#define int long long 
using namespace std ;
const int kmaxn = 1000 + 1 ;
const int inf = 2147483647 ; 
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
int n , vis[kmaxn][kmaxn] , f[kmaxn][kmaxn] , sx , sy , tx , ty , ans = inf;
int dx[4] = {0 , 0 , 1 , -1} ;
int dy[4] = {1 , -1 , 0 , 0} ;
struct node
{
	int x , y , step ;
}; 
queue<node> q ; 
bool check(int x , int y)
{
	return ( x <= n && y<=n && x >= 1 && y >= 1) ;
}
void search(node now)
{
	q.push(now) ;
	while(!q.empty())
	{
		node u = q.front() ;
		q.pop() ;
		int x = u.x , y = u.y , step = u.step ;
		if(step >= ans) continue ; //小小的优化,截去不符合条件的搜索枝 
		if(u.x == tx && u.y == ty)
		{
			ans = min(ans , step) ;
		}
		for(int i = 0 ; i < 4 ; i++)
		{
			int kx = x + dx[i] ;
			int ky = y + dy[i] ;
			if(!check(kx , ky)) continue ; //检查边界 
			if(vis[kx][ky]) continue ;
			vis[kx][ky] = 1 ;
			node nxt = {kx , ky , step + 1} ;
			q.push(nxt) ;
		}
	}
}
signed main() 
{
	//freopen("grid.in" , "r"  , stdin) ;
	//freopen("grid.out" , "w" , stdout) ;
	n = read() ;
	for(int i = 1 ; i <= n ; i++)
	{
		for(int j = 1 ; j <= n ; j++)
		{
			vis[i][j] = read() ;
		}
	}
	sx = read() , sy = read() , tx = read() , ty = read() ;
	if(vis[sx][sy] || vis[tx][ty]) 
	{
		cout <<"-1" ;
		return 0 ;
	}
	node ss = {sx , sy , 0} ;
	search(ss) ;
	if(ans != inf) printf("%lld" , ans) ;
	else printf("-1") ; 
	return 0 ;
}

【T3】

【题目描述】 :

戈兰斜是一种在带数字的网格上玩的日本拼图游戏。目标是在网格的每个单元格中绘制对角线,连接到每个格点的对角线个数等于他对应的数字。另外,禁止对角线形成环。

第 一个 图给 出了游戏的初始状态。 第二个图给出了对应的一个解答。数据保证问题一定存在至少一解。

【输入】:

输 入的 第一 行包含一个的单个整数 \(n\)表示棋盘的尺寸,棋盘是一个正方形。
然后紧接 \(n+1\)行。包含网格的初始状态。每行为一个含\(n+1\)个字符的字符串,字符要么为一个数字,要么为一个(‘.’),
其中数字都是\(0\)\(4\)之间的任意整数,‘.’表示连接到此格点的对角线数没有限制

【输出】:

输出包含 \(n\)行,每行 \(n\) 个字符,每个字符为斜杠或反斜杠表示如何填充相应的棋盘。

【数据范围】 :

对于 \(100%\)的数据 , \(n\leq 7\)

【考场】:

没什么很好的思路,准确的来说,没思路, 就不放代码了。

【正解思路】:

首先 , 这个问题分为四个部分 , 判断是否有回路 , 是否符合对角线的限制 ,对角线的判断 , 和搜索 ,这四个模块写完, 也就\(OK\)了。

1.有回路 : 我们可以设一个并查集, 只要让他们不会有斜着交叉的对角线出现,那么构成回路的条件也就只有4个顶点的父亲都是一个点了。

2.是否符合对角线的限制 , 我们设数组\(m\)表示原来的限制, 我们再设一个\(cur\)表示现在放了多少 , \(lim\)表示还能放多少 , 然后判断一下即可

3.对角线的判断 : 可以分两块进行求解 , 一块是从左上角到右下角的这一类的斜线 , 另一块是从右上角到左下角的这一类的斜线 ;

4.搜索 : 注意回溯 , 然后类比一下普通的棋盘,那么走就行了, 但是我们更新的时候是从左到右, 从上到下更新的。所以\(dx_i\)\(dy_i\)应当注意一下

【STd】:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
int chgx[4]={0,0,1,1},chgy[4]={0,1,0,1};
int n,ok;
int dad[101],ps[101][101],sum[101][101],a[101][101],cz[101][101];

int getnum()
{
  char c=getchar();
  while(!((c>='0' && c<='4') || c=='.')) c=getchar();
  if(c=='.') return -1;
  else return c-'0';
}

int findad(int x)
{
  if(!dad[x]) return x;
  return findad(dad[x]);
}

int psbl(int x,int y)
{
  if(a[x][y]==-1) return true;
  if(sum[x][y]<=a[x][y] && sum[x][y]+ps[x][y]>=a[x][y]) return true;
  return false;
}

void dfs(int x,int y)
{
  if(y==n) y=1,x++;
  if(x==n) 
    {
	 ok=1;
	  return;
	  }
  int t1,t2,i,pd=0;
  ++sum[x][y],++sum[x+1][y+1],--ps[x][y],--ps[x+1][y+1],--ps[x+1][y],--ps[x][y+1];
  for(i=0;i<4;i++) 
    if(!psbl(x+chgx[i],y+chgy[i])) {pd=1;break;}
  if(!pd)
  {
  t1=findad((x-1)*n+y),t2=findad(x*n+y+1);
  if(t1!=t2) 
  {
   cz[x][y]=0;
   dad[t1]=t2;
   dfs(x,y+1);
   if(ok) return;
   dad[t1]=0;
  }
  }
  --sum[x][y],--sum[x+1][y+1],++sum[x+1][y],++sum[x][y+1],pd=0;
  for(i=0;i<4;i++) 
    if(!psbl(x+chgx[i],y+chgy[i])) {pd=1;break;}
  if(!pd)
  {
  t1=findad((x)*n+y),t2=findad((x-1)*n+y+1);
  if(t1!=t2) 
  {
   cz[x][y]=1;
   dad[t1]=t2;
   dfs(x,y+1);
   if(ok) return;
   dad[t1]=0;
  }
  }
  --sum[x+1][y],--sum[x][y+1],++ps[x][y],++ps[x+1][y+1],++ps[x+1][y],++ps[x][y+1];
}

int main()
{
  int i,j,pd;
  freopen("gokigen.in","r",stdin);
  freopen("gokigen.out","w",stdout);
  scanf("%d",&n),n++;
  for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
     {
      ps[i][j]=4,a[i][j]=getnum(),pd=0;
      if(i==1 || i==n) ps[i][j]--,pd=1;
      if(j==1 || j==n) ps[i][j]--,pd=1;
      ps[i][j]-=pd;
	 }
  dfs(1,1);
  for(i=1;i<n;i++)
    {
	 for(j=1;j<n;j++) 
	  {
	   if(cz[i][j]) putchar('/');
	   else putchar('\\');
	  }
	 putchar('\n');
	}
}

【Code】:

/*
 by : Zmonarch
 知识点 : 搜索 
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <cstring>
#define int long long 
using namespace std ;
const int kmaxn = 1000 + 1 ;
const int inf = 2147483647 ; 
inline int read()
{
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
	return x * f ;
}
inline int getnum()
{
	char c = getchar() ;
	while (!(c == '.' || (c >= '0' && c <= '4'))) c = getchar() ;
	if(c == '.') return - 1 ;
	else return c - '0' ;
}
const int dx[4] = {0 , 0 , 1 , 1} ;//判定4个方位 
const int dy[4] = {0 , 1 , 0 , 1} ;//从左向右 , 从上到下依次更新 
int n ;
bool flag , ans[101][101] ; 
int point_to[kmaxn] , m[101][101] , cur[101][101] , lim[101][101] ;
// m用来存原始棋盘 , point_to表示的是父亲是谁 ,lim表示最多有几条对角线, cur表示现在有几条对角线 
void prepare()  // 有多组数据 
{
	flag = 0 ; 
	memset(cur , 0 , sizeof(cur)) ;
	memset(point_to , 0 , sizeof(point_to)) ;
}
bool board(int x , int y)
{
	if(m[x][y] == -1) return 1 ;//对于没有限制的点, 自然可以
	if(cur[x][y] <= m[x][y] && cur[x][y] + lim[x][y] >= m[x][y]) return 1; 
	//如果当前的对角线数已经到了目标数目, 那么停止,否则就继续 
	return 0 ;
}
int find(int x ) //寻找其父亲 
{
	if(!point_to[x]) return x ;
	return find(point_to[x]) ;
}
void search(int x , int y)
{
	if(y == n) y = 1 , x ++ ;//继续下一行 
	if(x == n) { flag = 1 ; return ; } //下一行就没有了 
	int t1 , t2 , pd = 0 ; 
	//先处理左上到右下的斜线 
	cur[x][y]++ , cur[x + 1][y + 1]++;  
	lim[x][y]-- , lim[x + 1 ][y]-- , lim[x][y + 1]-- , lim[x + 1][y + 1]--;
	for(int i = 0 ; i < 4 ; i++) //继续向下更新 
	{
		int kx = x + dx[i] , ky = y + dy[i] ;
		if(!board(kx , ky)) 
		{
			pd = 1 ; 
			break ;
		}
	} 
	if(!pd)
	{
		t1 = find( (x - 1) * n + y ) , t2 = find(x * n + y + 1) ;
		// (x-1) * n + y是左上的编号 , x*n + y+1右下的编号   
		if(t1 != t2) //不在一个并查集中 
		{
			ans[x][y] = 1 ;
			point_to[t1] = t2 ;
			search(x , y + 1) ;
			if(flag) return ;
			point_to[t1] = 0 ; // 回溯 
		}	
	}  
	cur[x][y]-- , cur[x + 1][y + 1]-- ;//令其回溯 
	//右上到坐下的斜线 
	cur[x + 1][y]++ , cur[x][y + 1]++ ;
	pd = 0 ;
	for(int i = 0 ; i < 4 ; i++)
	{
		int kx = x + dx[i] , ky = y + dy[i] ;
		if(!board(kx , ky)) 
		{
			pd = 1 ; break ;
		} 
	}
	if(!pd)
	{
		t1 = find( x * n + y ) , t2 = find( (x - 1) * n + y + 1 ) ;
		if(t1 != t2 )//对于有没有环,看这个点的连边是否为4条 
		{
			ans[x][y] = 0 ;
			point_to[t1] = t2 ;
			search(x , y + 1) ;
			if(flag) return ;
			point_to[t1] = 0 ;
		}
	}
	cur[x + 1][y] -- , cur[x][y + 1] --;
	lim[x][y]++ , lim[x + 1][y]++, lim[x][y + 1] ++ , lim[x + 1][y + 1]++ ;
}
signed main()
{
	int t = read() ;
	while(t--)
	{
		prepare() ;
		n = read() , n++;
		for(int i = 1 ; i <= n ; i++)
		{
			for(int j = 1 ; j <= n ;j++)
			{
				lim[i][j] = 4 ;
				m[i][j] = getnum() ;
				//处理边边角角 
				if((i == 1 || i== n) && (j ==1 || j == n)) // 处理角 
				{
					lim[i][j] = 1 ;
					continue ;
				}
				if(i == 1 || i == n || j == 1 || j == n)//处理边 
				{
					lim[i][j] = 2 ;
					continue ;
				}
			}
		}
		search(1 , 1);//从最左上角开始搜索 
		for(int i = 1 ; i < n ;i++)
		{
			for(int j = 1 ; j < n ; j++)
			{
				if(!ans[i][j]) printf("/");
				else printf("\\");
			}
			printf("\n") ;
		}
	}
	return 0 ;
}
posted @ 2021-01-03 11:52  SkyFairy  阅读(142)  评论(0编辑  收藏  举报