-----------------------题目:一步之遥
思路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;
}