0x02 枚举、模拟、递推

题目链接: 递归实现指数型枚举

解题思路:

  • dfs

state | 1 << u:将第u位设置为1,表示当前u包含在子集当中

  • 1 << u: 这是一个左移操作,表示将数字 1 左移 u 位。左移 u 位相当于在 1 后面加上 u 个 0。例如,1 << 0 是 1(二进制 0001),1 << 1 是 2(二进制 0010),1 << 2 是 4(二进制 0100),依此类推。这个操作的结果是一个只有第 u 位是 1 的数,其余位都是 0。
  • state | (1 << u): 这是一个按位或操作,表示将 state 的二进制表示与 1 << u 的结果按位或。按位或操作的规则是:如果某一位上有一个 1,结果在该位上就是 1。例如,假设 state 是 5(二进制 0101),u 是 1,那么 1 << 1 是 2(二进制 0010)。按位或操作 0101 | 0010 的结果是 0111,即 7。
#include<iostream>
using namespace std;
int n;
void dfs(int u, int state){//从第0个数开始枚举,state表示状态
    if(u == n){
    //当前所有数字都被考虑过了,输出当前子集并返回
        for(int i = 0; i < n; i++){//输出当前子集:0 - n-1的所有位
            if(state >> i & 1){ //检查state的第i位是否为1,如果是输出对应的数字 i+1
                cout << i + 1 << ' ';
            }
        }
        cout << endl;
        return;
    }
    dfs(u + 1, state);//不包含当前数字u,直接进入下一层递归
    dfs(u + 1 , state | 1 << u);//包含当前数字,将u对应位设置为1,然后进入下一层递归
    
}
int main(){
    cin >> n;
    dfs(0, 0);
    return 0;
}

题目链接:递归实现组合型枚举

思路分析:

  • dfs

  • 限制了输出的个数

  • 考虑边界条件

#include<iostream>
using namespace std;
int n, m;
void dfs(int u, int sum, int state){
    if(sum + n - u < m){// 当前选择的数字 + 没选的所有数字 都不够m个数字,直接返回
        return;
    }
    if(sum == m){
        for(int i = 0; i < n; i++){
            if(state >> i & 1){
                cout << i + 1 << " ";
            }
        }
        cout << endl;
        return;
    }
    //已经遍历n个了,还没有找到合法的情况,直接返回
    if(u == n){
        return;
    }
    dfs(u + 1, sum + 1, state | 1 << u);//先选:题目要求从小到大输出
    dfs(u + 1, sum, state);
    
    
}
int main(){

    cin >> n >> m;
    dfs(0, 0, 0);// 枚举了多少个数字,当前选了多少数字,当前选了哪些数
    return 0;
}

题目链接:递归实现排列型枚举

解题思路:

相当于有n个坑位,填充坑位的时候,从当前没有选择的数字中进行选择,直到将n个数字都放入n个坑位当中为之。

#include<iostream>
#include<vector>
using namespace std;
int n;
vector<int> path;
void dfs(int u, int state){
    if(u == n){
       for(auto x : path){
           cout << x << ' ';
       } 
       cout << endl;
       return;
    }
    for(int i = 0; i < n; i++){
        if(!(state >> i & 1)){//该数字没有枚举过
            path.push_back(i + 1);
            dfs(u + 1, state | 1 << i);//将该位置标记已经访问(二进制位置1),进入下一个位置的判断
            path.pop_back();// 恢复现场
        }
    }
    
    
}
int main(){
    cin >> n;
    dfs(0, 0);//从第0个数字开始枚举 每个数字是否选过用一个数字的二进制表示
    return 0;
}

题目链接:激光炸弹

解题思路

  • 前缀和
#include<iostream>
using namespace std;
const int N = 5010;

int g[N][N];
int n,m,w,r;
int main()
{
    cin >> n >> r;
    int x,y,w;
    for(int i=0;i<n;i++){
        cin >> x >> y >> w;
        g[x+1][y+1]+=w;
    }

    for(int i=1;i<=5001;i++){
        for(int j=1;j<=5001;j++){
            g[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1]+g[i][j];
        }

    }
 
    int ans=0;
    if(r>=5000){
        cout<<g[5001][5001]<<endl;
        return 0;
    }
    for(int i=r;i<=5001;i++){
        for(int j=r;j<=5001;j++){
            ans=max(ans,g[i][j]-g[i-r][j]-g[i][j-r]+g[i-r][j-r]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值