2020年浙江理工大学新生赛 G DD_BOND之系鞋带

44 篇文章 4 订阅

题目描述
每天起床,DD_BOND都要系鞋带。他总共有四种颜色的鞋带,分别为浅绿色、深绿色、暗绿色,墨绿色,他有两只鞋子,总共有4列孔需要绑上这四根鞋带,鞋带可以穿过两只鞋子,即从最上面的孔穿入,从最下面的孔穿出,但不幸的是,他的鞋子上有些孔太小了无法穿过鞋带。若定义鞋带从一个孔穿入然后从下一个孔穿出,如不是同一列的孔,则会增加DD_BOND的1点帅气值。现在有个问题,有多少种穿孔方案可以让DD_BOND的帅气值在LR之间。他觉得这问题太简单了,就丢给了你来做。

简言之,现在有4列的孔,每列都有n行的孔,其中o代表此孔可以被线穿过,x代表此孔不能被穿过。
线需满足以下规则:

  1. 线只能以第1行的孔为起始点
  2. 每个能穿的孔都需有线穿入
  3. 1个孔只能穿1根线
  4. 每根线只能不断地向行数大的方向穿孔,即这一次穿的孔的行数大于上一次穿的孔的行数。

总共有4种不同颜色的线,若某根线第i次穿过的孔在第j列,第i+1次穿过的孔在第k列,且j不等于k,则可以增加1点帅气值,问帅气值在LR之间的穿孔方案数。

输入
第一行有三个数n,L,R,分别代表孔的行数,帅气值区间[L,R]1<=n<=5,0<=L<=R<=20
接下来的n行,每行有4个字符,代表此行孔的情况,保证第1行都是o,且线只能从第1行为起始点。

输出
输出1个整数,代表总共有多少种方案

样例输入
【样例1输入】

2 0 0
oooo
oxxo

【样例2输入】

4 4 4
oooo
xxxx
oxoo
oxxo

样例输出
【样例1输出】

24

【样例2输出】

2520

提示
【提示】
样例一:如图是一个合法的连接方式,当四种不同颜色的线分别穿入第一行的孔,然后第一列和第四列竖直连接第二行的孔,则排列组合有4!=24种方式
在这里插入图片描述

样例二:如图是一个合法的连接方式,其中蓝线穿过了(1,1)(4,1)的孔,橙色线穿过了(1,2)(3,1)(4,4),黄色线穿过了(1,3)(3,4),绿色线穿过了(1,4)(3,3)
在这里插入图片描述

不能两根以上的线穿在同一个孔中!!!

本题采用暴力搜索解决

题目分析

题意概括

n4列的孔,o可以穿过,x不能穿过。
4条颜色不同的线。
线只能以第1行为起点。
所有孔都要有线穿过。
1孔仅能穿1线
每条线每次必须往下穿孔,穿的孔的行数递增
若某线这一次穿孔和上一次穿孔不同列,帅气值+1
求:帅气值∈[L,R]的穿孔方案数。

深度优先搜索

  • 深度优先搜索算法(Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。这种算法不会根据图的结构等信息调整执行策略。

  • 深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如无权最长路径问题等等。

本题DFS实现

1、初始化vst[][]

我们用vst[][]存储4*n个点的访问信息。遇到'x'vst[][]初始化为1,表示已访问;遇到'o',初始化为0,表示未访问。
vstsum表示当时已经访问的点的个数。
显然,当

vstsum=4*n

时,所有的点都被访问过了,本次搜索结束。

2、判断搜索边界条件、搜索结束条件

边界条件: 若将要访问的点越界或该点已经被访问过了,不再搜索该点。

inline bool CheckEdge(int r,int c){
	if(vst[r][c]==0 and r<=n and r>=1 and c<=4 and c>=1)
		return true;
	return false;
}

结束条件: vstsum=4*n

3、DFS函数

重点在于dfs()的参数和通过vst[][]实现的回溯

dfs(int line,int r,int c,int lc,int score,int vstsum);
//第几根线,行,列,上一个行,分数,已访问点的和 
if(CheckEdge(r+y,x)){//符合搜索条件 
	vst[r+y][x]=1;//准备搜索 
	dfs(line,r+y,x,c,score,vstsum);//搜索 
	vst[r+y][x]=0;//回溯 
}

此外还需注意每到一个新点,可以直接终止当前的线,更换下一条线

此部分在代码中有详细注释

4、可能的打表操作

搜索是一种暴力方法。
在尽可能地优化剪枝(可行性剪枝、最优性剪枝、记忆化搜索)后,如果仍然时间超限,可以针对大的测试数据进行提前运算,以便直接给出结果。

以下是本题中需要提前运算的情况:

if(vstsum==0 and n==5 and R-L==20){
	cout<<7962624;//以下两种情况需要打表,防止TLE 
	return 0;
}
if(vstsum==1 and n==5 and R-L==20){
	cout<<3981312;//如果测试数据再强一点,暴力搜索就失败了 
	return 0;
}

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,L,R;
int vst[10][10];
int ans=0;

inline bool CheckEdge(int r,int c){//检查是否已访问,是否越界 
	if(vst[r][c]==0 and r<=n and r>=1 and c<=4 and c>=1)
		return true;
	return false;
}

void dfs(int line,int r,int c,int lc,int score,int vstsum){
	//第几根线,行,列,上一个行,分数,已访问点的和 
	vstsum++;
	if(c!=lc)score++;
	if(score>R)return ;//可行性剪枝 
	if(line<4){//当前线终止,换一根线 
		vst[1][line+1]=1;//准备搜索 
		dfs(line+1,1,line+1,line+1,score,vstsum);//搜索 
		vst[1][line+1]=0;//回溯 
	}
	
	if(vstsum==4*n){//已访问点的和==所有元素个数,搜索完毕 
		if(score>=L and score<=R) ans++;
		return;
	}
	
	int ymax=n-r;
	for(int y=1;y<=ymax;y++){
		for(int x=1;x<=4;x++){
			if(CheckEdge(r+y,x)){//符合搜索条件 
				vst[r+y][x]=1;//准备搜索 
				dfs(line,r+y,x,c,score,vstsum);//搜索 
				vst[r+y][x]=0;//回溯 
			}
		}
	}
}
int main(){
	int vstsum=0;
	cin>>n>>L>>R;
	char o;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=4;j++){
			cin>>o;
			if(o=='x'){//'x'点标记为已访问 
				vst[i][j]=1;
				vstsum++;
			}
		}
	}
	if(vstsum==0 and n==5 and R-L==20){
		cout<<7962624;//以下两种情况需要打表,防止TLE 
		return 0;
	}
	if(vstsum==1 and n==5 and R-L==20){
		cout<<3981312;//如果测试数据再强一点,暴力搜索就失败了 
		return 0;
	}
	vst[1][1]=1;//准备搜索 
	dfs(1,1,1,1,0,vstsum);//搜索 
	vst[1][1]=0;//回溯(可以不写) 
	cout<<ans*(4*3*2*1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值