决赛第7届c++B组

-----------------------题目:一步之遥
在这里插入图片描述
思路1: 列方程暴力求

前进x步,后退y步,97x-127y=1 ,限制条件是x,y都大于1,找x+y的最小值
找个范围暴力求。

思路2: 扩展欧几里解方程。

97x=1mod(127) ,求出x后,(x%127+127)%127就是逆元。
输出 abs(x)+abs(y) 就是需要的值。

注意:
1.ax=1(mod b)a,b都是正整数, x(大于0)越大,y(小于0)就会越小 。
2.求逆元得到的x是一个满足的最小正整数。y是负数的话代表相反的方向,所以结果应该是向前移动的步数和向后移动的步数的和:abs(x)+abs(y)

#include <bits/stdc++.h>
using namespace std;
//1:暴力解方程:97x-127y=1 求x+y的最小值。
//2:扩展欧几里得解方程ax+by=c : 97x-127y=1
//3:求逆元97x=1(mod 127) ,求得的x是最小正整数 
int ex_gcd(int a, int b, int &x, int &y){
	if (b == 0){
		x = 1;
		y = 0;
		return a;
	}
	int gcd = ex_gcd(b, a % b, x, y);
	int tmp = x;
	x = y;
	y = tmp - a / b * y;
	return gcd;
	
} 
int main(){
	
	int x, y;
	//解方程 
	int gcd = ex_gcd(97, 127, x, y);
	x /= gcd;
	x = x * 1 / gcd;
	y = y * 1 / gcd;
	cout << abs(x) + abs(y) << endl;
	
	//求逆元 
	gcd = ex_gcd(97, 127, x, y);
	cout << abs(x) + abs(y) << endl;
	return 0;
}

-----------------------题目:凑平方数
在这里插入图片描述
思路:
step1: 把0到9876543210所有的平方数都得到,如果每个数字不重复就放到vector中。
step2:dfs搜索,每个数字只有选和不选两种选择,如果当前数字与之前数字状态某一位都为1则发生了重复,return ;终止条件是:状态0-9都为1。

#include <bits/stdc++.h>
using namespace std;
//先把所有不重复的数字搞出来放到vector中 
//然后dfs搜索所有情况:每个只能选一次
long long ans = 0;
vector<long long> v;
int len = 0;
bool judge(long long m){
	//判断该数字每一位是否重复 
	int status = 0;
	while (m){
		int bit = 1 << (m % 10);
		if (bit & status) return false;
		status = status | bit; 
		m /= 10;
	}
	return true;
}
int get_status(long long m){ 
	if (m == 0) return 1; 
	int status = 0;
	while (m){
		int bit = 1 << (m % 10);
		status = status | bit;
		m /= 10;
	}
	return status;
}
void dfs(int index, int status){ //下标,状态 
	
	if (status == (1 << 10) - 1) {  
		ans++;
		return ;
	}
	if (index == len) return ;
	int status1 = get_status(v[index]);
	if ((status1 & status) == 0) { 
		dfs(index + 1, status | status1); //选
	} 
	dfs(index + 1, status);				//不选 	
}

int main(){

	for (long long i = 0; i * i <= 9900000000; ++i){
		if (judge(i * i)) { //judge的参数也设置为 long long!!! 
			v.push_back(i * i);
			len++;
		}
	}
	dfs(0, 0); 
	cout << ans << endl;
	return 0;
} 
//300

-----------------------题目:机器人塔
在这里插入图片描述
在这里插入图片描述
思路: 考虑到下一层能够确定上一层,而上一层确定不了下一层,所以从最低层开始往上层走,因为 a*(1+a)/2=(m+n) 所以最底层的数目为(int)sqrt((m+n)*2)。因为A,B数目不确定(0->(int)sqrt((m+n)*2)),所以用二进制来枚举。异或得到上一层,每得到上一层,就对A,B的数目进行更新,不符合的直接return ;符合的会到达顶层。
注意:
1.a^(a>>1)得到上一层之后,需要把高位的1去掉。
2.查看状态中0/1的个数,可以算出1的个数,然后当前层的个数减去1的个数。

#include <bits/stdc++.h>
using namespace std;
/*
B(n):1 A(m):0
层数ceng:(1+ceng)*ceng/2== A和B的个数和(m+n)  ceng为最底层的个数
最底层开始进行二进制枚举 ;最底层确定后上面所有的摆法就会确定。 
*/
int ans = 0;
int count1(int i){
	int cnt = 0;
	while (i){
		cnt++;
		i &= (i - 1);
	}
	return cnt;
}
void dfs(int index, int val, int m, int n){ //一层一层递推,递推的层数不确定,用递归来做
	
	int cnt1 = count1(val);
	int cnt0 = index - cnt1;
	if (index == 1 && m - cnt0 == 0 && n - cnt1 == 0) {
		ans++;
		return ;
	}
	if (cnt1 > n || cnt0 > m){
		return ;
	}
	int next_val = (val ^ (val >> 1));
	next_val = next_val & ((1 << (index - 1)) - 1);
	dfs(index - 1, next_val, m - cnt0, n - cnt1);	
}
int main(){
	
	int m, n;// m:A n:B 
	cin >> m >> n;
	int ceng = (int)sqrt(2 * (m + n));
	
	for (int val = 0; val <= (1 << ceng) - 1; ++val){
		dfs(ceng, val, m, n);
	}
	cout << ans << endl;
	return 0;
} 

-----------------------题目:生成树计数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:暴力偏分->组合搜索顶点-1条边+并查集判断是否存在环来筛选

m:边的个数 n:顶点的个数
搜索边的个数为为Cm n-1,为边的个数的n-1次方,边个数很多时将会崩溃,正确的解法应该是DP,但是比较复杂,放弃了。。。

#include <bits/stdc++.h>
using namespace std;
/*
思路: 
dfs用组合搜索n*m-1条边(2^(m*n)),并查集判断是否构成环。
*/ 
struct Edge{
	int u;
	int v;
	Edge(int _u, int _v) : u(_u), v(_v) {}
}; //对边集进行搜索的话用边集进行存储,u和v都代表的是二维映射到一维的值 
vector<Edge> v;
vector<Edge> tmp;
char mp[7][100005]; 
int n, m, nm;
int len = 0;
int father[800000];
int ans = 0;

int get_id(int x, int y) {
	return (x - 1) * m + y;
}
int init(){
	for (int i = 1; i <= n * m; ++i){
		father[i] = i;
	}
}
int findfather(int m){
	if (father[m] == m) return father[m];
	return father[m] = findfather(father[m]);
}
void dfs(int index){
	
	if (tmp.size() == nm - 1){
		init();
		for (int i = 0; i < nm - 1; ++i){
			if (findfather(tmp[i].u) != findfather(tmp[i].v)){
				int fau = findfather(tmp[i].u);
				int fav = findfather(tmp[i].v);
				father[fau] = fav;
			}else return ;
		}
		ans++;
		return ; 
	}
	for (int i = index; i < len; ++i){
		tmp.push_back(v[i]);
		dfs(i + 1); 
		tmp.pop_back();
	}
}
int main(){
	
	cin >> n >> m;
	nm = n * m;
	for (int i = 1; i <= n; ++i){
		for (int j = 1; j <= m; ++j){
			cin >> mp[i][j]; 
			if (mp[i][j] == 'N') nm--; //顶点的个数不是nm,要去掉N的个数!!! 
		}
	} 
	//建图:行先建图,然后列再建图,边从小到大存一份就够了。 
	for (int i = 1; i <= n; ++i){ 
		for (int j = 1; j < m; ++j){
			if (mp[i][j] == 'E' && mp[i][j + 1] == 'E'){
				v.push_back(Edge(get_id(i, j), get_id(i, j + 1)));
				len++;
			}
		}
	}
	for (int j = 1; j <= m; ++j){
		for (int i = 1; i < n; ++i){
			if (mp[i][j] == 'E' && mp[i + 1][j] == 'E'){
				v.push_back(Edge(get_id(i, j), get_id(i + 1, j)));
				len++;
			}
		}
	}
	if (len < nm - 1) {
		cout << 0 << endl;
		return 0;
	} 
	dfs(0);
	cout << ans << endl;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值