蓝桥备赛第一周2021.1.11 递归 枚举 位运算

递归实现指数型枚举

链接:https://ac.nowcoder.com/acm/problem/50918
来源:牛客网

题目描述
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n \gt 0n>0, 0 \leq m \leq n0≤m≤n, n+(n-m)\leq 25n+(n−m)≤25。
输入描述:
两个整数n,m。
输出描述:
按照从小到大的顺序输出所有方案,每行1个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。

示例1
输入
复制
5 3
输出
复制
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
///回溯典型
int n,m;
int mem[maxn]={0};
void traceback(int val,int pos)///val是当前的选择(已经选了(相当于栈顶)),
                            ///pos是当前位置(还没有选)
{

    if(pos==m+1)
    {
        for(int i=1;i<=m;i++)
        {
            cout<<mem[i]<<" ";

        }
        cout<<endl;
    }

    ///我喜欢在选择的时候查约束
    for(int i=val+1;i<=n;i++)
    {
        if((pos+n-i)<m)///比如说一共取四个数,第三个数那就不能为n
        {
            continue;
        }
        mem[pos]=i;
        traceback(i,pos+1);
        ///不用撤回,直接覆盖了
    }




}

int main()
{
    cin>>n>>m;
    traceback(0,1);
    return 0;
}

递归指数型枚举

链接:https://ac.nowcoder.com/acm/problem/50911
来源:牛客网

题目描述
从 1\sim n1∼n这 n (n \leq 16)(n≤16) 个整数中随机选取任意多个,输出所有可能的选择方案。
输入描述:
一个整数n。
输出描述:
每行一种方案。同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。对于没有选任何数的方案,输出空行。本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
示例1
输入
复制
3
输出
复制
3

2
2 3
1
1 3
1 2
1 2 3

方法1:肯定是2^n行,所以直接就是上一个动态m从0到n加一堆空行

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
///回溯典型
int n,m;
int feikong=0;
int mem[maxn]={0};
void traceback(int val,int pos)///val是当前的选择(已经选了(相当于栈顶)),
                            ///pos是当前位置(还没有选)
{

    if(pos==m+1)
    {
        feikong++;
        for(int i=1;i<=m;i++)
        {
            cout<<mem[i]<<" ";

        }
        cout<<endl;
    }

    ///我喜欢在选择的时候查约束
    for(int i=val+1;i<=n;i++)
    {
        if((pos+n-i)<m)///比如说一共取四个数,第三个数那就不能为n
        {
            continue;
        }
        mem[pos]=i;
        traceback(i,pos+1);
        ///不用撤回,直接覆盖了
    }




}
int ercifnag(int q)
{
    int res=1;
    for(int i=0;i<q;i++)
    {
        res*=2;
    }
    return res;
}
int main()
{
    //cout<<ercifnag(2);
    cin>>n;
    ///?那就循环m呗
    for(m=1;m<=n;m++)
    {
        //cout<<m;
        traceback(0,1);
    }
    for(int i=0;i<ercifnag(n)-feikong;i++)
    {
        cout<<endl;
    }

    return 0;
}

方法2:以最新的值为n为结束,遇到为0的不输出,用完要恢复为0

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
///回溯典型
int n,m;
//int feikong=0;
int mem[maxn]={0};
void traceback(int val,int pos)///val是当前的选择(已经选了(相当于栈顶)),
                            ///pos是当前位置(还没有选)
{
    for(int i=1;i<=n;i++)
    {
        if(mem[i])///恢复状态使有的可以为0
        cout<<mem[i]<<" ";
    }
    cout<<endl;

    if(val==n)
    {
        return ;
        //feikong++;

    }

    ///我喜欢在选择的时候查约束
    for(int i=val+1;i<=n;i++)
    {
        if((pos+n-i)<m)///比如说一共取四个数,第三个数那就不能为n
        {
            continue;
        }
        mem[pos]=i;
        traceback(i,pos+1);
        mem[pos]=0;

    }




}
int ercifnag(int q)
{
    int res=1;
    for(int i=0;i<q;i++)
    {
        res*=2;
    }
    return res;
}
int main()
{
    cin>>n;
    traceback(0,1);
    return 0;
}

递归实现排列型枚举

链接:https://ac.nowcoder.com/acm/problem/50919
来源:牛客网

题目描述
把 1\sim n1∼n 这 n(n \lt 10)(n<10)个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入描述:
一个整数n。
输出描述:
按照从小到大的顺序输出所有方案,每行1个。 首先,同一行相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
示例1
输入
复制
3
输出
复制
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

这个思路应该是交换,先写下
懒得交换了,还是用记忆数组顺手,,,

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
///回溯典型
int n,m;
int mem[maxn]={0};
int mark[maxn]={0};
void traceback(int pos)
{
    if(pos>n)
    {
        for(int i=1;i<=n;i++)
        {
            cout<<mem[i]<<" ";
        }
        cout<<endl;
        return ;
    }

    for(int i=1;i<=n;i++)
    {
        if(mark[i]==1)
        {
            continue;
        }

        mem[pos]=i;
        mark[i]=1;
        traceback(pos+1);
        mark[i]=0;
    }



}

int main()
{
    cin>>n;

    traceback(1);
    return 0;
}

非递归组合枚举

链接:https://ac.nowcoder.com/acm/problem/50925
来源:牛客网

从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n \gt 0n>0, 0 \leq m \leq n0≤m≤n, n+(n-m)\leq 25n+(n−m)≤25。
输入描述:
两个整数n,m。
输出描述:
按照从小到大的顺序输出所有方案,每行1个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。

示例1
输入
复制
5 3
输出
复制
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

这个用了很多东西,递归+栈转栈+栈,state存状态等等

引下大佬的state解释
在这里插入图片描述

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
//set<int> s[maxn];
///不yang递归那就用栈吧
struct now{
    int pos,u,sum,state;
    ///pos
    ///state 表示选和不选(
    ///按我的老思路是把state下个数组或者栈,记录当前选择,
    ///不过看了一个大佬的挺牛的,一个变量存大量状态)
    ///正常递归是栈+递归
    ///不yang递归就是栈加栈嘛
};
stack<now>s;
int main()
{
    int n,m;
    cin>>n>>m;
    s.push({0,0,0,0});///和递归初始值一样
    while(s.size()){
        struct now t=s.top();
        s.pop();///上一步状态
        if(t.pos==0){
            if(t.sum+n-t.u<m)continue;///越界,不放回(边界条件)

            if(t.sum==m){///够了
                for(int i=0;i<n;i++)
                {

                    if(t.state>>i&1){
                            cout<<"*"<<t.state<<"*";
                        cout<<i+1<<" ";
                    }
                }
                cout<<endl;
                continue;
            }

            t.pos=1;
            s.push(t);
            s.push({0,t.u+1,t.sum+1,t.state|1<<t.u});
            continue;

        }
        if(t.pos==1){
            t.pos=2;
            s.push(t);
            s.push({0,t.u+1,t.sum,t.state});
            continue;
        }else{
            continue;
            }

    }

    return 0;
}

sumdiv

链接:https://ac.nowcoder.com/acm/problem/50922
来源:牛客网

题目描述
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^BA
B
. Determine S modulo 9901 (the rest of the division of S by 9901).
输入描述:
The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.
输出描述:
The only line of the output will contain S modulo 9901.
示例1
输入
复制
2 3
输出
复制
15
说明
2^3 = 8.2
3
=8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15.
15 modulo 9901 is 15 (that should be output).。

2的3次方的所有因子求和
类似快速幂(用了快速幂,总之是种二分)

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
///快速幂加求和

const int moder=9901;
int ksm(int a,int b)
{
    int res=1;
    a%=moder;
    while(b){
        if(b&1){
            res=(res*a)%moder;
        }
        a=(a*a)%moder;
        b/=2;
    }
    return res;
}
int sum(int p,int k){///快速幂变形
    if(k==0){
        return 1;

    }
    if(k%2==0)
    {
        return (p%moder*sum(p,k-1)+1)%moder;
    }

    return (1 + ksm(p, k / 2 + 1)) * sum(p, k / 2) % moder;

}


int main()
{
    int A,B;
    cin>>A>>B;
    int res = 1;
    for (int i = 2; i <= A; i++) {
        int s=0;
        while (A % i == 0) {
            s++;
            A /= i;
        }
        if (s) res = res * sum(i, s * B) % moder;
    }
    if(!A) res=0;
    cout << res << endl;

    return 0;
}

大佬解法,很清晰,我也喜欢,但有10%数据过不去

https://blog.csdn.net/yeguxin/article/details/47017955

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
using namespace std;
 
const int h = 10001;
int n,m;
struct node
{
    int x;
    int y;
}q[h];
 
long long int power(long long int pn,long long int pm)   ///反复平方法来求A^B  省时间
{
    long long int sq = 1;
    while(pm>0)
    {
        if(pm%2)
        {
            sq = (sq*pn)%9901;
        }
        pm = pm / 2;
        pn = pn * pn % 9901;
    }
    return sq;
}
 
long long int updata(long long int pn,long long int pm)  ///递归二分求等比数列的和
///对每一个质因子求1+p1+p1^2+p1^3+...p1^k1,正好用等比数列,等比数列就可以递归
{
    if(pm == 0)
    {
        return 1;
    }
    if(pm%2)
    {
        return (updata(pn,pm/2)*(1+power(pn,pm/2+1)))%9901;  /// 当pm为奇数时,有公式来求等比数列的和  (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1)) 
    }
    else
    {
        return (updata(pn,pm/2-1)*(1+power(pn,pm/2+1)) + power(pn,pm/2))%9901;  ///当pm为偶数时,有公式来求等比数列的和  (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2); 
    }
}
 
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int k = 0;
        for(int i=2;i*i<=n;)   ///寻找质因子,一个很好的方法
        {
            if(n%i == 0)
            {
                q[k].x = i;///q[k].x q[k].y是质因子和次数
                q[k].y = 0;
                while(n%i == 0)
                {
                    q[k].y++;
                    n /= i;
                }
                k++;
            }
            if(i == 2)
            {
                i++;///2和3是要算的
            }
            else
            {
                i = i + 2;///这样就不用算偶数了
            }
        }
        if(n!=1)///最后一个是本身
        {
            q[k].x = n;
            q[k].y = 1;
            k++;
        }
        int ans = 1;
        for(int i=0;i<k;i++)
        {       
            ans = (ans*(updata(q[i].x,q[i].y*m)%9901)%9901);
            ///既然是n的m次方,那么就直接用n的质因子m次方就好,
            ///这里相乘是约数和公式,对每一个因子求(1+p1+p1^2+p1^3+...p1^k1) ,
        	///然后相乘
        }
        printf("%d\n",ans);
    }
    return 0;
}

费解的开关

链接:https://ac.nowcoder.com/acm/problem/50920
来源:牛客网

题目描述
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

10111
01101
10111
10000
11011

在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。

输入描述:
第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n \leq 5n≤5;
对于100%的数据,n \leq 500n≤500。
输出描述:
输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。
示例1
输入
复制
3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111
输出
复制
3
2
-1

https://blog.csdn.net/weixin_42176113/article/details/107271626
这个里面的位运算写法就是

 for(int k=0; k<(1<<5); k++) 

用5位的k来存第一行的操作状态
比如k=10010
而下面这行代码就是遍历k的每一位,看他是不是1

for(int i=0; i<5; i++) 
        {
            if(k>>i&1)
            {
                turn(0,i);
                cnt++;
            }
        }

1.上面的位运算

2.memcpy

3.memcpy的两个数组类型不能串!!!!!!,我人都傻了,把k给cpy了也不报个错

4.还有bfs解法等我缓缓再弄吧

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
const int INF=1e7+5;
int dx[5]={0,-1,0,1,0};
int dy[5]={0,0,1,0,-1};
int myCheese[10][10];///这题一看暴力回溯必超时啊,像dp那么倒推?先试试魔方遍历
//char backup[10][10];//!!!!!!!!!!!就tm这句改了一个点
int backup[10][10];
void init_cheese(){
    for(int i=0;i<6;i++)
    {
        for(int j=0;j<6;j++)
        {
            myCheese[i][j]=0;
        }
    }
}
void click(int x,int y){

   for(int i=0; i<5; i++)
    {
        int tx=x+dx[i],ty=y+dy[i];
        if(tx>=0 && tx<5 && ty>=0 && ty<5)
        {
            myCheese[tx][ty]+=1;
            myCheese[tx][ty]%=2;
        }
    }

}
//int

int mofang()
{
    int ans=INF;
    for(int k=0;k<(1<<5);k++)
    {
        int cnt=0;

        //cout<<sizeof(myCheese)<<endl;
        //cout<<sizeof(int)*36<<endl;
        //cout<<k<<endl;
        memcpy(backup,myCheese,sizeof(myCheese));

        ///第一行32种点击可能,由本次尝试的k决定
        for(int i=0;i<5;i++)
        {
            //cout<<"old k"<<k<<endl;
            if(k>>i&1)
            {

                click(0,i);
                cnt++;
            }
           // cout<<"new k"<<k<<endl;
        }

        ///从0号行到3号行,有0的就点下一行
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<5;j++)
            {
                if(myCheese[i][j]==0)
                {
                    click(i+1,j);
                    cnt++;
                }
            }
        }

        ///检查最后一行
        int flag=1;
        for(int i=0;i<5;i++)
        {
            if(myCheese[4][i]==0)
            {
                flag=0;
                break;
            }
        }
        if(flag==1)
        {
            ans=min(ans,cnt);
        }
        memcpy(myCheese,backup,sizeof(myCheese));

    }

    if(ans>6)
    {
        ans=-1;
    }
    return ans;

}
int main()
{
    int N;
    cin>>N;
    string buf;
    while(N--)
    {
        for(int i=0;i<5;i++)
        {
            cin>>buf;
            //cout<<"buf:"<<buf<<endl;
            for(int j=0;j<5;j++)
            {
                myCheese[i][j]=buf[j]-'0';
              //  myCheese[i][j]=(buf>>(5-1-j))&1;
            }

        }

//        for(int i=0;i<5;i++)
//        {
//
//            for(int j=0;j<5;j++)
//            {
//                cout<<myCheese[i][j]<<" ";
//            }
//            cout<<endl;
//        }
        cout<<mofang()<<endl;;

    }
    return 0;
}

翻硬币

链接:https://ac.nowcoder.com/acm/problem/14355?&headNav=acm
来源:牛客网

题目描述
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:oo*oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作,那么要求:

输入描述:
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000
输出描述:
一个整数,表示最小操作步数。
示例1
输入
复制


oo
输出
复制
5
示例2
输入
复制
ooo***
ooo***
输出
复制
1

蓝桥原题,似乎是个智力题?
超级进阶:
https://www.cnblogs.com/kuangbin/p/3218060.html

思路:

1.模拟翻硬币的过程
2.记录不相同的位置,规律:相邻两个不相同硬币的位置差即为这两个之间需要翻转的次数

思路

/思路:每一次不一样肯定要翻两枚枚硬币,直到都一样为止

模拟下:(没办法啊,肯定都得翻,贪心咯)

#include<stdio.h>
#include<string.h>
char String1[2000];
char String2[2000];
int main()
{
    int i,n,sum;
    scanf("%s %s",String1,String2);
    n=strlen(String1);
    sum=0;
    for(i=0;i<n;i++)
    {
       if(String1[i]!=String2[i]){
           if(String2[i+1]=='*'){
              String2[i+1]='o';
           }else{
              String2[i+1]='*';  
           }
           sum++;
       }
    }
    printf("%d\n",sum);
    return 0;
}

Strange Towers of Hanoi

链接:https://ac.nowcoder.com/acm/problem/50921
来源:牛客网

盘子个数取1-12的四汉诺塔

先用四塔算法,取固定1-n个(k)个,将剩下的用四塔算法从A-》B,然后,用三塔算法,从A-》D,然后用四塔算法从B-》D
但,最快的解法是用递推式:

法一:递推式

https://blog.csdn.net/yanglei253/article/details/93310024

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

int main()
{
    int three[13],four[13];
    // 最初初始化为 0x3f 在最大流中是有很大用处的,这里虽然用不到
    memset(four,0x3f,sizeof(four));
    three[0] = 0;
    three[1] = 1;
    for(int i = 2;i < 13;i++)
    {
        three[i] = 2 * three[i - 1] + 1;
    }
    four[0] = 0;
    for(int i = 0;i < 13;i++)
    {
        for(int j = 0;j < i;j++)
        {
            four[i] = min(four[i],2 * four[j] + three[i - j]);
        }
    }
    
    for(int i = 1;i < 13;i++)
    {
        cout << four[i] << endl;
    }
    
    return 0;
}

dp:

https://blog.csdn.net/SSL_ZYC/article/details/81611372

#include <cstdio>
#include <iostream>
#define Inf 1e9
using namespace std;

int f[5][21];

int main()
{
    f[3][1]=f[4][1]=1;
    for (int i=2;i<=12;i++)
    {
        f[3][i]=f[3][i-1]*2+1;  //初始化三个柱子的情况
        f[4][i]=Inf;
    }
    printf("1\n");  //一个盘子
    for (int i=2;i<=12;i++)
    {
        for (int j=1;j<=i;j++)
         f[4][i]=min(f[4][j]*2+f[3][i-j],f[4][i]);
        printf("%d\n",f[4][i]);
    }
    return 0;
}

带分数

第一个想法

遍历小于N的所有数
100:1+99 2+98 。。。。。14+86
然后把前面的数标记进mark,后面的数用dfs造分数检查
其中加点剪枝
更好的思路:枚举整数和分母,求分子
https://moomhxy.blog.csdn.net/article/details/88768920?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control

但这个代码其实不是bfs,就是纯暴力,大晚上写不动了
我的200多ms憨憨代码

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005



bool judger(int a, int b, int c)
{   //统计abc 中每位数出现的次数
    bool f[12] = {false};
    while(a)
    {
        int tmp = a % 10;
        if(f[tmp] == true)
            return false;
        f[tmp] = true;
        a /= 10;
    }
    while(b)
    {
        int tmp = b % 10;
        if(f[tmp] == true)
            return false;
        f[tmp] = true;
        b /= 10;
    }
    while(c)
    {
        int tmp = c % 10;
        if(f[tmp] == true)
            return false;
        f[tmp] = true;
        c /= 10;
    }
    if(f[0] == true)  //如果有0出现
        return false;
    for(int i = 1; i <= 9; ++i)
        if(!f[i])
            return false;
    return true;
}

int conter(int n)
{
    int ans=0;

    for(int i=1;i<n;i++)
    {
        for(int j=1;j<1e4+5;j++)
        {
            int k=(n-i)*j;///分子
            if(i!=j&&k>=j)///这里可以在上面遍历的时候剪枝,但我懒了
            {
                if(judger(i,j,k))
                {
                    ans++;
                }
            }
        }
    }

    return ans;
}


int main()
{
    int N;
    cin>>N;
    cout<<N<<" "<<conter(N)<<endl;

    return 0;
}

大佬的dfs 46ms代码

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=46455914

#include <bits/stdc++.h>
using namespace std;
const int N=20;
int n;
int res=0;
bool st[N],mark[N];
bool check(int a,int c)
{
    int b=n*c-a*c;
    if(!a||!b||!c) return false;
    memcpy(mark,st,sizeof(st));
    while(b)
    {
        int x=b%10;
        b=b/10;
        if(!x||mark[x]) return false;
        mark[x]=true;
    }
    for(int i=1;i<=9;i++)
    {
        if(!mark[i]) return false;
    }
    return true;
}
void dfs_c(int u,int a,int c)
{
    if(u>9) return;
    if(check(a,c)) res++;
    for(int i=1;i<=9;i++)
    {
        if(!st[i])
        {
            st[i]=true;
            dfs_c(u+1,a,c*10+i);
            st[i]=false;
        }
    }
}
void dfs_a(int u,int a) //u表示当前用到了几个数,a表示当前枚举到的a的值
{
    if(a>=n) return;
    if(a) dfs_c(u,a,0); //带分数中三个数都不能是0,在这里剪枝
    for(int i=1;i<=9;i++)
    {
        if(!st[i])
        {
            st[i]=true;
            dfs_a(u+1,a*10+i);
            st[i]=false;
        }
    }
}
int main()
{
    cin>>n;
    dfs_a(0,0);
    cout<<n<<" ";
    cout<<res<<endl;
    return 0;
}

Fractal Streets

又是自闭的分型,上次已经留下了深深的心理阴影

大意:

https://blog.csdn.net/xuechen_gemgirl/article/details/87975396
题面大概意思是:
   给你一个原始的分形图,t组数据,对于每组数据,输入3个数n,h,o (n为在第n级,h,o为两个房子的编号),求在第n级情况下,编号为h和o的两个点之间的距离*10为多少。
  其中,第n级分形图形成规则如下:

首先先在右下角和右上角复制一遍n-1情况下的分形图
然后将n-1情况下的分形图顺时针旋转90度,放到左上角
最后将n-1情况下的分形图逆时针旋转90度 ,放到左下角
编号是从左上角那个点开始计1,沿着道路计数。
还有这说法的吗,我大意了啊,没有闪(另外第二张图右上角标号错了)

<< (n-1)其实就是乘2的n-1次方

https://blog.csdn.net/ben_xsy/article/details/79288058

另外两个博客里面对第四块坐标转换一个+1一个-1是因为一个从0开始一个从1开始
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define maxn 1005
#define ll long long

ll p[40];///打打表,每一层最大值,别重复算了
void rec(ll n,ll s,ll &x,ll &y)
{
    if(n==1)///翻到最底层了
    {
        if(s==1){
            x=1;
            y=1;
        }
        else if(s==2)
        {
            x=1;
            y=2;
        }
        else if(s==4)///小地方不要犯错
        {
            x=2;
            y=1;
        }
        else {
            x=2;
            y=2;
        }
        return ;
    }

    if(s<=p[n-1])///左上角
    {
        rec(n-1,s,x,y);///左上角要顺时针90de
        ll t;
        t=x;
        x=y;
        y=t;
    }
    else if(s<=2*p[n-1])///右上角
    {
        rec(n-1,s-p[n-1],x,y);///是倒推
        y+=(1ll<<(n-1));///用下面的求出下一层的x与y后,本层要加上本层的偏移
    }else if(s<=3*p[n-1]){///右下角
        rec(n-1,s-2*p[n-1],x,y);
        x+=(1ll<<(n-1));
        y+=(1ll<<(n-1));
    }else{///左下
        rec(n-1,s-3*p[n-1],x,y);///x与y是什么受下层影响,所以也可以不管,直接输x,y,然后
        ///下面两个式子赋值的时候把x和y互换
        ///(这个是优化后的了)
        ll xyuan,yyuan;
        xyuan=x;
        yyuan=y;
        x=(1ll<<n)+1-yyuan;
        y=(1ll<<(n-1))+1-xyuan;
    }


}


int main()
{

    int t;
    cin>>t;
    p[1]=4;
    for(int i=2;i<=39;i++){
        p[i]=p[i-1]*4;
       // cout<<p[i]<<endl;
    }
    ll n,A,B,x1,y1,x2,y2;

    while(t--){
        cin>>n>>A>>B;
        rec(n,A,x1,y1);
        rec(n,B,x2,y2);
        printf("%.0lf\n",sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))*10);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值