• 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
    " ,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
    " ,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 imes 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%)的数据 , (nleq 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('
    ');
    	}
    }
    

    【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("
    ") ;
    		}
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    线程池中shutdown()和shutdownNow()方法的区别
    java.util.concurrent.TimeoutException: Idle timeout expired: 300000/300000 ms
    ConnectTimeout和ReadTimeout所代表的意义
    hive 查询注意问题
    java面试题之int和Integer的区别
    MySQL中KEY、PRIMARY KEY、UNIQUE KEY、INDEX 的区别
    MySQL数据库使用mysqldump导出数据详解
    C++宏定义详解
    OTL调用存储过程/函数及注意事项
    linux自定义开机启动服务和chkconfig使用方法
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/14225122.html
Copyright © 2020-2023  润新知