递推与递归专题练习

CH0301 递归实现指数型枚举

搜索与回溯,指数级算法。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
using namespace std;
vector<int>chosen;
int n;
void dfs(int x){
    if(x==n+1){
        for(unsigned i=0;i<chosen.size();++i)
            printf("%d ",chosen[i]);
        puts("");
        return;
    }
    dfs(x+1);
    chosen.push_back(x);
    dfs(x+1);
    chosen.pop_back();
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n);
    dfs(1);
    return 0;
}

CH0302 递归/非递归实现组合型枚举

搜索与回溯。要字典序最小,所以优先往加入当前节点的分支搜索。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
using namespace std;
vector<int> chosen;
unsigned n,m;
void dfs(unsigned x){
    if(chosen.size()>m||chosen.size()+(n-x+1)<m)
        return;
    if(x==n+1){
        for(unsigned i=0;i<chosen.size();++i)
            printf("%d ",chosen[i]);
        puts("");
        return;
    }
    chosen.push_back(x);
    dfs(x+1);
    chosen.pop_back();
    dfs(x+1);
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n),read(m);
    dfs(1);
    return 0;
}

CH0303 递归实现排列型枚举

搜索与回溯

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
int n,order[11];
bool chosen[11];
void dfs(int x){
    if(x==n+1){
        for(int i=1;i<=n;++i)
            printf("%d ",order[i]);
        puts("");
        return;
    }
    for(int i=1;i<=n;++i)if(!chosen[i]){
        chosen[i]=1,order[x]=i;
        dfs(x+1);
        chosen[i]=0;
    }
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(n);
    dfs(1);
    return 0;
}

CH0201 费解的开关

描述

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

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

10111
01101
10111
10000
11011

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

01111
11101
10111
10000
11011

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

01111
11001
11001
10100
11011

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

做法

游戏有三个性质:

  1. 每个位置至多被点击一次。
  2. 若固定的第一行的点法,则满足题意的方案至多有一种。
  3. 点击的顺序不影响最终结果。

于是枚举第一行的点法,从第一行开始递推。

时间复杂度\(O(2^n n^2)\)。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
co int N=10;
int g[N][N],n,ans,tmp[N][N];
void change(int x,int y){
    tmp[x][y]^=1;
    if(x-1) tmp[x-1][y]^=1;
    if(y-1) tmp[x][y-1]^=1;
    if(y+1<=n) tmp[x][y+1]^=1;
    if(x+1<=n) tmp[x+1][y]^=1;
}
int t;
char s[N];
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    read(t);
    n=5;
    while(t--){
        ans=25;
        for(int i=1;i<=n;++i){
            scanf("%s",s+1);
            for(int j=1;j<=n;++j)   
                g[i][j]=(s[j]-'0')^1;
        }
        for(int s=0;s<(1<<n);++s){
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                    tmp[i][j]=g[i][j];
            bool ok=1;
            int tmp=s,k=1,cnt=0;
            while(tmp){
                if(tmp&1) change(1,k),++cnt;
                ++k,tmp>>=1;
            }
            for(int j=2;j<=n;++j)
                for(k=1;k<=n;++k)if(::tmp[j-1][k])
                    change(j,k),++cnt;
            for(int j=1;j<=n;++j) if(::tmp[n][j]){
                ok=0;
                break;
            }
            if(ok) ans=std::min(ans,cnt);
        }
        if(ans>6) puts("-1");
            else printf("%d\n",ans);
    }
    return 0;
}
上一篇:[USACO09HOL]假期绘画Holiday Painting


下一篇:[CQOI2012]组装 (贪心)