首页 > 其他分享 >济南 CSP-J 刷题营 Day2 搜索

济南 CSP-J 刷题营 Day2 搜索

时间:2023-08-17 09:03:38浏览次数:45  
标签:node int Day2 long dfs 答案 CSP 向量 刷题

Solution

T1 排列计数

原题链接

4077: 排列计数

简要思路

直接用 next_permutation 枚举全排列计算答案即可。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

int n,k;
int a[100];
int ans;//可能的答案数量

signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		a[i]=i;//先记录每个数

	do{
		bool f=true;

		for(int i=2;i<=n;i++)
			if(abs(a[i]-a[i-1])==k)//任意相邻两数的差的绝对值不能为 k
				f=false;

		if(f==1)ans++;//满足条件

	}while(next_permutation(a+1,a+n+1));//do-while 循环枚举每一种可能的排列
	cout<<ans<<endl;
	return 0;
}

T2 向量乘法

原题链接

4078: 向量乘法

简要思路

  • No.1 80pts
    同 T1,枚举全排列按照题目中给定的计算方式来计算最终的结果,并记录一个桶来计算有几个不同的答案。

  • No.2 100pts
    发现题目给定的式子等同于 \((α_{p_1} \times α_{p_2}) \times (α_{p_3} \times α_{p_4}) \times (α_{p_5} \times α_{p_6}) ......\),也就是说第 \(2k-1\) 和第 \(2k\) 个向量交换顺序不会影响计算结果,那么每对向量放在哪里都不会影响计算结果。

    按照上述性质和结论,我们此题可以用 DFS。用一个 set 来储存答案,DFS 两个数:当前遍历到了第几对向量以及当前的计算结果。维护一个数组 \(f\),\(f_i\) 表示是否轮到了第 \(i\) 个向量。如果 \(f_i\) 为 \(0\),就对其进行 DFS。注意 truefalse 的变化。

完整代码

  • No.1 80pts
#include<bits/stdc++.h>
using namespace std;

map<int,bool> m;//map 储存答案
int p[10];//为每个向量打编号,以来全排列
struct node{
	int x;
	int y;
}a[105];//向量结构体

int js(node a,node b){//奇数情况下,向量 a * 向量 b 为一个实数 r
	node c;
	c.x=a.x*b.x;
	c.y=a.y*b.y;
	int r;
	r=c.x+c.y;
	return r;
}

node os(int r,node b){//偶数情况下,实数 r * 向量 b 为一个向量 c
	node c;
	c.x=b.x*r;
	c.y=b.y*r;
	return c;
}

int f,num,n;

signed main(){
	cin>>n;
	n*=2;
	for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
	for(int i=1;i<=n;i++) p[i]=i;//给每组向量打编号

	do{
		int ans=0;//计算答案
		node k;
		k=a[p[1]];//记录当前排列状态下的第一组向量是什么
		
		for(int i=1;i<n;i++){
			if(i%2!=0) ans=js(k,a[p[i+1]]);//奇数
			if(i%2==0) k=os(ans,a[p[i+1]]);//偶数
		}
		
		if(m[ans]!=1)num++;//如果这个答案是第一次出现就将最终答案 num++
		m[ans]=1;//标记为已经出现
		
	}while(next_permutation(p+1,p+1+n));

	cout<<num<<endl;
	return 0;
}
  • No.2 100pts
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=25;

bool used[MAXN];
int x[MAXN],y[MAXN];
set<int> s;//存储答案(set 有自动去重的功能)
int n;

void dfs(int i,int now){//dfs 到了第 i 个向量,且之前向量的计算结果为 now
	while(i<2*n&&used[i])//轮到了 i
		i++;
		
	if(i==2*n){//结算完毕
		s.insert(now);//插入结果
		return;
	}
	used[i]=true;//标记轮到了
	
	for(int j=i+1;j<2*n;j++)//枚举后面每一种可能的向量
		if (!used[j]){//如果没轮到
			used[j]=true;//轮到 true
			dfs(i,now*(x[i]*x[j]+y[i]*y[j]));
			//此处 i 不用加一,因为上面的 while 循环已经对 i 进行了更新
			//通过给定的向量乘法的公式更新 now
			used[j]=false;//dfs 完后更新为 false,以为了下一组 dfs
		}
	
	used[i]=false;//注意更新为 false
}

signed main(){
	cin>>n;
	for(int i=0;i<2*n;i++)
		cin>>x[i]>>y[i];//输入 2n 个向量
		
	dfs(0,1);//dfs 第 0 个向量,当前答案为 1(因为要乘法,所以初始值是 1 不是 0)
	
	cout<<s.size()<<endl;//有几个不同的答案
}

T3 数字博弈

原题链接

4079: 数字博弈

简要思路

这是一道博弈题。DFS 搜索,搜索的两个状态就是两个人的当前数字,直接暴力搜索,可以发现状态并不多(详见代码注释)。

完整代码

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,a,b;

int dfs(int a,int b){
	if(a>n||b>n)return a-b;//如果有人的答案超出 n,就返回两人的差
	else return max(-dfs(b,a+b),-dfs(b,a*b));
	//因为每个人都要保证解对于自己来说尽可能地优,所以返回为 max
	//因为下一步遍历的是对方选数,所以取负数
	//同上,下一步为对方选择方案,所以状态一为 b,状态二为两数的 和/积
}

signed main(){
	cin>>n>>a>>b;
	cout<<dfs(a,b)<<endl;//直接 dfs
	return 0;
}

T4 放置棋子

原题链接

4080: 放置棋子

简要思路

发现:

  1. 当 \(n \not= m\) 的时候,答案为 \(0\);
  2. 当 \(k = 0\) 的时候,答案为 \(1\)。

考虑将矩阵的每一行按照 01 串的字典序进行排序。如果两个矩阵排序后的矩阵相同,那么它们属于同一个等价类。搜索这样的等价类,每个类对答案的贡献都是一个组合数。

考虑用 meet-in-middle 双向搜索进行优化。

But,正解是按照上述算法进行打表。有一个小技巧:表是对称的,这样的话可以加快打表的速度。

完整代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

int n,m,k;
int ans[10][10]={{1},//n=1
                {1, 1},//n=2
                {1, 2, 1},//n=3
                {1, 6, 6, 1},//n=4
                {1, 24, 90, 24, 1},//n=5
                {1, 120, 2040, 2040, 120, 1},//n=6
                {1, 720, 67950, 297200, 67950, 720, 1},//n=7
                {1, 5040, 3110940, 68938800, 68938800, 3110940, 5040, 1},//n=8
                {1, 40320, 187530840, 24046189440ll, 116963796250ll, 24046189440ll, 187530840, 40320, 1},//n=9
                {1, 362880, 14398171200ll, 12025780892160ll, 315031400802720ll, 315031400802720ll,12025780892160ll, 14398171200ll, 362880, 1}};//n=10
//要保证 k<n,n=m,所以可以减少打表的数量以及去除 m 这一维度的打表

signed main(){
  cin>>n>>m>>k;
  
  //先特判不符合题意的情况
  if(k==0){
    cout<<1<<endl;
    return 0;
  }
  
  if(n!=m||k>n){
    cout<<0<<endl;
    return 0;
  }
  
  cout<<ans[n][k]<<endl;//输出打表程序
  return 0;
}

标签:node,int,Day2,long,dfs,答案,CSP,向量,刷题
From: https://www.cnblogs.com/CheZiHe929/p/17636638.html

相关文章

  • CSP模拟22
    火批专场。骨架、灌伤、虚化、闪光只为碎银几两看世人慌慌张张只为碎银几两偏偏这碎银几两能解万种惆怅世人啊匆匆忙忙徒为碎银几两奈何这碎银几两让人心神荡漾A.骨架考虑点的贡献异常麻烦,我们可以把点的贡献转化为边的贡献。对于一条边,我们有如下几点:伴随着......
  • 刷题记录(三)
    攻防世界-Confusion1打开环境,主页导航有三个选项,其中注册和登陆页面报错404,但提示了flag的位置在首页的url后面添加{{1+1}}发现系统存在SSTI漏洞。使用经典payload进行尝试:''.__class__.__mro__[2].__subclasses__()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt'......
  • javascript学习笔记day2
    今天在b站跟学了黑马的前端js课程,因为是第一天学习都对于我们这种学过了的来说其实挺简单的,不过今天一边做公司的项目一边学习多少是有点时间不够的感觉,看样子明天要开二倍看了,下面是今天的笔记什么是js:javascript是人机交互的一种编程语言js由哪几部分组成:ECMAScript和webapis......
  • 中电金信通过KCSP认证 云原生能力获权威认可
    ​中电金信通过KCSP(KubernetesCertifiedServiceProvider)认证,正式成为CNCF(云原生计算基金会)官方认证的Kubernetes服务提供商。 ​ Kubernetes是容器管理编排引擎,底层实现为容器技术,是云原生领域最关键技术之一。作为全球最大的云计算社区——CNCF的第一个毕业项目,Kub......
  • CSP模拟21
    CSP模拟21T1GetP5999把跳的顺序转换为填数。对于一个位置,两边填的数都要小于或都大于它才符合题意。我们按照从小到大的顺序插入数字,这样保证填的位置左右都小于它。设\(dp_{i,j}\)表示填了\(i\)个数,分成了\(j\)个块的方案数。考虑添加一个数,我们有三种情况。\(i\)......
  • 【刷题笔记】23. Merge k Sorted Lists
    题目Mergeksortedlinkedlistsandreturnitasonesortedlist.Analyzeanddescribeitscomplexity.Example:Input:[1->4->5,1->3->4,2->6]Output:1->1->2->3->4->4->5->6题目大意合并K个有序链表解题思路借助分治的思想,把K个......
  • 华为认证考试每日刷题与解析 Part1
    1、如图所示,路由器RE分别存在去往三个SiteA、SiteB和Site_C三个网段的静态路由,管理员需要配置将Site_A和Site_B路由引入至0SPFDomain中,请将以下配置RE的命令补全。1:______2:______3:______?(英文,全小写)答案:permit,permit,if-match解析:常用命令2、针对MAC地址欺骗atta......
  • csp模拟<反思>3
    csp模拟21ARC141F首先上结论:如果一个串能用其他串消完那么这个串可以删去;剩下的串中有\(S_i\)是\(S_j\)的子串,那么答案是Yes;如果存在\(S_i=A+B\)和\(S_j=B+C\),且\(A\neqC\)则答案是Yes.第一部分:如何判断一个串\(S_i\)是否能被消完。建AC自动机,预处理\(ma......
  • 用户在某天刷题后第二天再来刷题的平均概率
    限定条件:第二天再来。解法1:表里的数据可以看作是全部第一天来刷题了的,那么我们需要构造出第二天来了的字段,因此可以考虑用leftjoin把第二天来了的拼起来,限定第二天来了的可以用date_add(date1,interval1day)=date2筛选,并用device_id限定是同一个用户。解法2:用lead函数将同一用......
  • Day26(2023.08.10)
    行程8:45    到达上海市信息安全测评认证中心(黄浦区陆家浜路1308号)9:00  Windows核查11:30--13:00   吃饭休息13:00 Windows核查17:00      下班   其中lusrmgr.msc      本地用户和组gpedit.msc......