MCS_ACM DFS解题报告

本文分享了在ACM比赛中使用DFS解决棋盘问题的思路,包括按行回溯搜索的方法以及如何通过枚举和剪枝优化计算答案。在Problem D和E中,通过剪枝技巧解决了超时问题,特别是应用了奇偶剪枝来提高效率。
摘要由CSDN通过智能技术生成

Problem A:棋盘问题
题目大意:给N*N大小棋盘,M个棋子,问要将棋子放在棋盘中的’#’位置有几种方法..
思路:按行回溯搜索..

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
 
int n, k, sum;
char m[10][10];
bool col[10];		//记录行状态 
 
void dfs(int cur, int cnt) {
	if(cnt == 0) {
		sum++;		//若棋子恰好放置完全,则方法数加一 
	}
	for(int i = cur + 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			if(m[i][j] == '#' && col[j] == false) {
				col[j] = true;		//该行已放置棋子 
				dfs(i, cnt - 1);	//放置后进入i层搜索 
				col[j] = false;		//用于回溯 
			} 
		}
	}
}
 
int main() {
	while(scanf("%d %d", &n, &k) != EOF) {
		sum = 0;
		if(n == -1 && k == -1) {
			break;
		}
		for(int i = 1; i <= n; i++) {
			scanf("%s", m[i] + 1);
		}
		memset(col, false, sizeof col);
		dfs(0, k);		//从0行开始搜索 
		printf("%d\n", sum);
	} 
	return 0;
} 
Problem B:Find The Multiple
题目大意 :找出某个数的倍数且该倍数只由0和1组成..

思路:计算发现,答案其实可以用ull存放,枚举0和1,DFS..

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;

int n;
unsigned long long ans;		//测试数据过大,需使用ull存储 
bool flag;

void dfs(unsigned long long cur, int deep) {	//deep为我们搜索数字的长度 
	if(cur % n == 0) {		//若找到可以进行整除操作的数,则退出 
		flag = true;
		ans = cur;
		return ;
	}
	if(deep == 19) return ;		//由样例输出得知,答案最长18位,若越界则退出 
	if(!flag) dfs(cur * 10, deep + 1);		//下一位枚举0 
	if(!flag) dfs(cur * 10 + 1, deep + 1);	//下一位枚举1 
}

int main() {
	while(scanf("%llu", &n), n) {
		flag = false;
		dfs(1, 0);	//从1开始搜索 
		printf("%llu\n", ans);
	}
	return 0;
}

附上打表代码:

#include <stdio.h>
int main()
{
	char a[200][30] = {{"1"},{"10"},{"111"},{"100"},{"10"},{"1110"},{"1001"},{"1000"},{"111111111"},{"10"},{"11"},{"11100"},{"1001"},{"10010"},{"1110"},{"10000"},{"11101"},{"1111111110"},{"11001"},{"100"},{"10101"},{"110"},{"110101"},{"111000"},{"100"},{"10010"},{"1101111111"},{"100100"},{"1101101"},{"1110"},{"111011"},{"100000"},{"111111"},{"111010"},{"10010"},{"11111111100"},{"111"},{"110010"},{"10101"},{"1000"},{"11111"},{"101010"},{"1101101"},{"1100"},{"1111111110"},{"1101010"},{"10011"},{"1110000"},{"1100001"},{"100"},{"100011"},{"100100"},{"100011"},{"11011111110"},{"110"},{"1001000"},{"11001"},{"11011010"},{"11011111"},{"11100"},{"100101"},{"1110110"},{"1111011111"},{"1000000"},{"10010"},{"1111110"},{"1101011"},{"1110100"},{"10000101"},{"10010"},{"10011"},{"111111111000"},{"10001"},{"1110"},{"11100"},{"1100100"},{"1001"},{"101010"},{"10010011"},{"10000"},{"1111111101"},{"111110"},{"101011"},{"1010100"},{"111010"},{"11011010"},{"11010111"},{"11000"},{"11010101"},{"1111111110"},{"1001"},{"11010100"},{"10000011"},{"100110"},{"110010"},{"11100000"},{"11100001"},{"11000010"},{"111111111111111111"},{"100"},{"101"},{"1000110"},{"11100001"},{"1001000"},{"101010"},{"1000110"},{"100010011"},{"110111111100"},{"1001010111"},{"110"},{"111"},{"10010000"},{"1011011"},{"110010"},{"1101010"},{"110110100"},{"10101111111"},{"110111110"},{"100111011"},{"111000"},{"11011"},{"1001010"},{"10001100111"},{"11101100"},{"1000"},{"11110111110"},{"11010011"},{"10000000"},{"100100001"},{"10010"},{"101001"},{"11111100"},{"11101111"},{"11010110"},{"11011111110"},{"11101000"},{"10001"},{"100001010"},{"110110101"},{"100100"},{"10011"},{"100110"},{"1001"},{"1111111110000"},{"11011010"},{"100010"},{"1100001"},{"11100"},{"110111"},{"11100"},{"1110001"},{"11001000"},{"10111110111"},{"10010"},{"1110110"},{"1010100"},{"10101101011"},{"100100110"},{"100011"},{"100000"},{"11101111"},{"11111111010"},{"1010111"},{"1111100"},{"1111110"},{"1010110"},{"11111011"},{"10101000"},{"10111101"},{"111010"},{"1111011111"},{"110110100"},{"1011001101"},{"110101110"},{"100100"},{"110000"},{"100101111"},{"110101010"},{"11010111"},{"11111111100"},{"1001111"},{"10010"},{"100101"},{"110101000"},{"1110"},{"100000110"},{"1001011"},{"1001100"},{"1010111010111"},{"110010"},{"11101111"},{"111000000"},{"11001"},{"111000010"},{"101010"},{"110000100"},{"1101000101"},{"1111111111111111110"},{"111000011"},{"1000"}};
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		if(n == 0)
		break;
		printf("%s\n", a[n - 1]);
	}
	return 0;
}

Problem C:   Oil Deposits

题目大意 :找有几块油田,连在一起的算一块..
思路 :八个方向搜,DFS.
#include<stdio.h>
#include<string.h>
char a[111][111];
int s,m,n;
void dfs(int x,int y)
{
	if(a[x][y]=='*'||x<0||y<0||x>m-1||y>n-1)
		return;//如果不是油田或者超出就返回 
	else
	{
			a[x][y] = '*';//找到就标记掉 
			dfs(x-1,y-1);
			dfs(x-1,y);
			dfs(x-1,y+1);
			dfs(x,y-1);
			dfs(x,y+1);
			dfs(x+1,y-1);
			dfs(x+1,y);
			dfs(x+1,y+1);//八个方向搜	
	}
}
int main()
{
	int i,j;
	while(scanf("%d %d",&m,&n)!=EOF,m||n)
	{
		for(i=0;i<m;i++)
		{
			scanf("%s",a[i]);
		} 
		s=0;//记录有几块 
        for(i=0;i<m;i++)  
        {  
            for(j=0;j<n;j++)  
            {  
                if(a[i][j]=='@')//找到一块,开始扩展 
                {  
                    dfs(i,j);  
                    s++;//找完一次,总数加一 
                }  
            }  
        }
		printf("%d\n",s);      
	}
	return 0;
}

Problem D:Red and Black

题目大意 @为起点,可以上下左右四个方向扩展,只能走'.',问能走过多少个点..
思路 :上一题的八个方向改为四个,DFS..

#include<stdio.h>
#include<string.h>
char a[111][111];
int s,m,n;
void dfs(int x,int y)
{
	if(a[x][y]=='#'||x<0||y<0||x>n-1||y>m-1)
		return;//如果不是点或者超出就返回 
	else
	{
		a[x][y] = '#';//找到就标记掉(起点第一次就标记掉,总数加一)
		s++;//标记完点数量加一 
		dfs(x-1,y);		
		dfs(x,y-1);
		dfs(x,y+1);
		dfs(x+1,y);	//四个方向搜
	}
}
int main()
{
	int i,j,x,y;
	while(scanf("%d %d",&m,&n)!=EOF,m||n)
	{
		for(i=0;i<n;i++)
		{
			scanf("%s",a[i]);
		} 
		s=0;//记录点数量 
        for(i=0;i<n;i++)  
        {  
            for(j=0;j<m;j++)  
            {  
                if(a[i][j]=='@')  
                {  
                    x=i;
                    y=j; //找到起点,记录坐标 
                } 
            }  
        }
        dfs(x,y);//从坐标开始搜 
		printf("%d\n",s);      
	}
	return 0;
}

Problem E:Tempter of the Bone

题目大意 S为起点,D为终点,问能否恰好在t时刻到达终点..
思路 :DFS+奇偶剪枝,普通深搜会超时,要剪枝..

TLE之后改了好久,才知道要剪枝.不剪枝过不掉啊..

奇偶剪枝:

若 t-[abs(ex-sx)+abs(ey-sy)] 结果为非偶数(奇数),则无法在t步恰好到达;
返回,false;
反之亦反..

#include<stdio.h>
#include<string.h>
#include<math.h>
int a[10][10];
char ma[10][10];
int i,j,n,m,t,x,y,flag,ex,ey,s;
void dfs(int x,int y)
{
	if(flag==1)//能走到,返回 
		return;
	if(s>t)//超过了还没到,返回 
		return;
	if(x==ex&&y==ey&&s==t)//到达终点坐标,且在t时到,标记能,返回 
	{
		flag=1;
        return;
	}
	if((x-1)>=0&&(x-1)<n&&y>=0&&y<m&&a[x-1][y]==0&&ma[x-1][y]!='X')//向上 
    {
        a[x-1][y]=1;
		s++;
        dfs(x-1,y);
        a[x-1][y]=0;//不能要重置回去 
        s--;
    }
    if((x+1)>=0&&(x+1)<n&&y>=0&&y<m&&a[x+1][y]==0&&ma[x+1][y]!='X')//向下 
    {
        a[x+1][y]=1;
		s++;
        dfs(x+1,y);
        a[x+1][y]=0;
        s--;
    }
	if(x>=0&&x<n&&(y-1)>=0&&(y-1)<m&&a[x][y-1]==0&&ma[x][y-1]!='X')//向左 
    {
        a[x][y-1]=1;
		s++;
        dfs(x,y-1);
        a[x][y-1]=0;
        s--;
    }
	if(x>=0&&x<n&&(y+1)>=0&&(y+1)<m&&a[x][y+1]==0&&ma[x][y+1]!='X')//向右 
    {
        a[x][y+1]=1;
		s++;
        dfs(x,y+1);
        a[x][y+1]=0;
    	s--;
    }	
}
int main()
{
	while(scanf("%d %d %d",&n,&m,&t)!=EOF,m||n||t)
	{
		getchar();
		for(i=0;i<n;i++)
		{
			scanf("%s",ma[i]); 
			for(j=0;j<m;j++)
			{
				if(ma[i][j]=='S')
				{
					x=i;
					y=j;//记录起点坐标 
				}
				if(ma[i][j]=='D')
                {
                    ex=i;
                    ey=j;//记录终点坐标 
                }				
			}
		}
		memset(a,0,sizeof(a));
		a[x][y]=1;
		flag=0;//标记是否能到达 
		s=0;
		if((abs(x-ex)+abs(y-ey)-t)%2!=0)//奇偶剪枝, 关键 !!!
		{
			printf("NO\n");
		}
		else
		{
			dfs(x,y);
			if(flag)
  				printf("YES\n");
       		else
        		printf("NO\n");
		} 
	}
	return 0;
}

Problem F:Prime Ring Problem
题目大意 :将1~n个数无序连成环,使得该环相邻两个数之和为素数。
思路 :环的第一个数总为1,我们只需枚举当前位置的下一个数字,使得该数字与当前位置数字之和为素数,即可进入下个位置的搜索,到达位置n时,若位置n上的数与第一个位置上的数字(其实就是1)的和若也为素数,则我们搜索到了合法解,输出即可..

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int a[25];		//用于存储答案 
bool b[25];		//记录数字使用状态 
int n;

//素数判断,千万别写错。。。 
bool prime(int m) {		
	for(int i = 2; i * i <= m; i++) {
		if(m % i == 0) {
			return false;
		}
	}
	return true;
}

void dfs(int deep) {
	if(deep == n) {		//若深度已到达n,即可进行合法性判断 
		if(prime(1 + a[n])) {		//判断最后一个数和第一个数的和是否为素数 
			for(int i = 1; i <= n; i++) {		//若符合,则输出答案 
				printf(i == 1 ? "%d" : " %d", a[i]);
			}
			printf("\n");
		}
		return;
	}
	for(int i = 2; i <= n; i++) {
		//若枚举的数字未被使用,且与当前deep深度位置数字之和为素数 
		if(b[i] == false && prime(a[deep] + i)) {		
			b[i] = true;		//表示这个数字已被使用 
			a[deep + 1] = i;	//将这个数添加进答案数组 
			dfs(deep + 1);		//深度+1,继续搜索 
			b[i] = false;		//用于回溯,其他可 
		}
	}
}

int main() {
	int cnt = 0;
	while(scanf("%d", &n) != EOF) {
		memset(a, 0, sizeof a);
		memset(b, false, sizeof b);
		a[1] = 1;
		b[1] = true;		//1总是第一位数字,已被使用 
		printf("Case %d:\n", ++cnt);
		dfs(1);		//第一位数字即为第1层深度 
		printf("\n");
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值