DFS搜索习题

DFS搜索

      • B. Coffee Chicken(递推定义 、递归)
      • E. Hilbert Sort(递推定义 + 递归)
      • F Partition problem (DFS搜索 + 注意优化方式)
      • D. Counting Sequences I (在数组 n 上做搜索)
      • Problem L. World Cup (DFS 预处理结果)
      • Sticks POJ - 1011( DFS搜索 )
      • Square POJ - 2362(数组 n 上做 0 、1选择)
      • Unit Fraction Partition POJ - 1980(DFS搜索)
      • Beat HDU - 2614( 二维数组上搜索)
      • 符号三角形 HDU - 2510 (数组 n 上做 0 、1选择)

B. Coffee Chicken(递推定义 、递归)

DFS搜索习题_第1张图片
链接
题意:给定斐波那契形式的字符串递推公式,然后让你输出第 n 个字符串从第 k 个字符开始的 10个字符。

思路

  • 从当前层递归到上一层的位置
  • 预处理字符串的长度,注意使用 long long。
  • 可以看成是一颗二叉树,跑的时候,就是判断一下在左边还是右边,到达S1或S2后输出所求位置的字符。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=500+10,MAX=1e12+10;

ll dp[maxn];
string s1="COFFEE";
string s2="CHICKEN";

char dfs(int n,ll k)
{
    if(n==1) return s1[k-1];
    if(n==2) return s2[k-1];
    if(k<=dp[n-2]) return dfs(n-2,k);
    else return dfs(n-1,k-dp[n-2]);
}

int main()
{
    dp[1]=6,dp[2]=7;
    for(int i=3;i<=500;++i)
    {
        dp[i]=dp[i-2]+dp[i-1];
        dp[i]=min(dp[i],MAX);
    }
    ll t,n,k;
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        for(ll i=k;i<=k+9&&i<=dp[n];++i)
            cout<<dfs(n,i);
        puts("");
    }
    return 0;
}

E. Hilbert Sort(递推定义 + 递归)

DFS搜索习题_第2张图片
链接

题意:给定曲线递归后的定义,然后让你给第 n 层曲线上的一些点,让你按访问的先后顺序输出这些点。
思路

  • 思路和上一题一样,从当前层的位置,递归到上一层
  • 处理一下转换后的位置即可。位置转换的对应,可以把当前层的位置和上一层的位置对应来看。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1e6+10,MAX=1e12;
int n,k;
ll d[maxn];
struct Point
{
    int x,y,id;
    bool operator<(const Point & b) const
    {
        return id<b.id;
    }
}p[maxn];
ll dfs(int x,int y,int k)
{
    if(k==0) return 1;
    ll mid=1<<k-1;
    ll s=mid*mid;
    if(x<=mid&&y<=mid)
        return dfs(y,x,k-1);
    else if(x>mid&&y<=mid)
        return s+dfs(x-mid,y,k-1);
    else if(x>mid&&y>mid)
        return 2*s+dfs(x-mid,y-mid,k-1);
    else if(x<=mid&&y>mid)
        return 3*s+dfs(mid-(y-mid)+1,mid-x+1,k-1);
}
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;++i)
    {
        cin>>p[i].x>>p[i].y;
        p[i].id=dfs(p[i].x,p[i].y,k);
    }
    sort(p+1,p+1+n);
    for(int i=1;i<=n;++i)
        cout<<p[i].x<<" "<<p[i].y<<"\n";
    return 0;
}

F Partition problem (DFS搜索 + 注意优化方式)

DFS搜索习题_第3张图片
链接

题意:给定 2n个人,每两个人之间有一个竞争值,将这 2n个人分配在两个队伍里,使得竞争值最大。只计算不同队伍,每两个人之间的竞争值。

思路

  • n 最大为14, C 28 14 C_{28}^{14} C2814在1e7左右。所以可以用DFS枚举每一种情况
  • 这里需要注意优化方法,在最后统计的时候,会多出 ( 2 n ) 2 (2n)^2 (2n)2 的时间复杂度
  • 因此我们可以边转移边统计竞争值,当我们在选择的集合中加入一个人的时候,需要取消他对同集合中的人的竞争值,同时要添加他对剩余集合中的人的竞争值。这样就是只剩 2n 的复杂度了。
  • 为什么这样优化后只剩下2n的复杂度?因为我们对于每一个人只有选与不选两种情况,每个人只会加入集合一次,因此每个人对其他人的贡献也只需要统计一次。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1e6+10,MAX=1e12;

int n;
int v[30][30];
ll ans;

void dfs(int p,int mask,int k,ll cost)
{
    if(k==n)
    {
        ans=max(ans,cost);
        return;
    }
    for(int i=p;i<=2*n;++i)
    {
        ll tmp=cost;
        for(int j=1;j<=2*n;++j)
        {
            if(mask>>j-1&1)
                tmp-=v[i][j];
            else tmp+=v[i][j];
        }
        dfs(i+1,mask+(1<<i-1),k+1,tmp);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=2*n;++i)
        for(int j=1;j<=2*n;++j)
            scanf("%d",&v[i][j]);

    ll cost=0;
    for(int j=1;j<=2*n;++j)
        cost+=v[1][j];
    dfs(2,1,1,cost);
    printf("%lld\n",ans);
    return 0;
}

D. Counting Sequences I (在数组 n 上做搜索)

DFS搜索习题_第4张图片
链接

思路:

  • a数组从全 1 开始搜索,搜索出所有可能的答案。
  • 只需要搜索出一种组合即可,所以搜索答案时,设 a 1 ≤ a 2 ≤ ⋯ ≤ a k a_1\le a_2\le \dots \le a_k a1a2ak,剩余的数用 1 填充。
  • 剪枝:
  1. 当所填的最大数 lim > n,直接返回,因为出现一个 n+1和 2 ,和等于2n,积等于 2n+2,这样就无法满足了。
  2. 当前的 乘积 大于 总和加上剩余位置补1,无法满足条件
  3. 当前的 乘积*当前放置的数lim 大于 总和,无法满足条件
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1e6+10,mod=1e9+7;
int qpow(int b,int n,int mod)
{
    int res=1;
    while(n)
    {
        if(n&1) res=1ll*res*b%mod;
        b=1ll*b*b%mod;
        n>>=1;
    }
    return res;
}
const int N=3000;
int fac[N+10],finv[N+10];
void init()
{
    fac[0]=fac[1]=1;
    for(int i=2;i<=N;++i)
        fac[i]=1ll*fac[i-1]*i%mod;
    finv[N]=qpow(fac[N],mod-2,mod);
    for(int i=N-1;i>=0;--i)
        finv[i]=1ll*finv[i+1]*(i+1)%mod;
}
int C(int n,int m)
{
    return 1ll*fac[n]*finv[m]%mod*finv[n-m]%mod;
}
int t,n;
int dfs(int lim,int p,int pro,int sum,int cnt,int now)
{
    if(p==n+1)
    {
        if(pro==sum)
            return 1ll*finv[cnt]*now%mod*fac[n]%mod;
        return 0;
    }
    if(pro==sum+n-p+1)
        return 1ll*now*finv[cnt]%mod*finv[n-p+1]%mod*fac[n]%mod;
    if(pro>sum+n-p+1) return 0;
    if(pro*lim>sum+lim+n-p) return 0;
    if(lim>n) return 0;
    int ans=0;
    ans=(ans+dfs(lim,p+1,pro*lim,sum+lim,cnt+1,now))%mod;
    ans=(ans+dfs(lim+1,p,pro,sum,0,1ll*now*finv[cnt]%mod))%mod;
    return ans;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        int ans=dfs(2,1,1,0,0,1);
        printf("%d\n",ans);
    }
    return 0;
}

Problem L. World Cup (DFS 预处理结果)

DFS搜索习题_第5张图片
链接
题意:有 4 个队伍进行 6场比赛,赢的加3分,输的不加分。如果是平局双方各加一分。现在给定 4 个队伍最终的分数,问能不能根据分数还原出原来的比赛胜负情况。只有一种情况输出yes,多种情况输出no,分数不合理输出Wrong Scoreboard.
思路

  • dfs 预处理所有结果得到的分数情况
  • 细节:自己采用10进制压位的时候,要保证 a、b、c、d小于10。不然会产生进位
#include <bits/stdc++.h>
#define ll long long
using namespace std;

int t,a,b,c,d;
int x[10],visit[200000];

int calc(int a,int b,int c,int d)
{
    return a*1000+b*100+c*10+d;
}

void f(int x,int &a,int &b)
{
    if(x==0) a+=3;
    else if(x==1) b+=3;
    else if(x==2) a++,b++;
}

void dfs(int k)
{
    if(k==7)
    {
        int a=0,b=0,c=0,d=0;
        f(x[1],a,b);
        f(x[2],a,c);
        f(x[3],a,d);
        f(x[4],b,c);
        f(x[5],b,d);
        f(x[6],c,d);
        int res=calc(a,b,c,d);
        visit[res]++;
        return;
    }
    for(int i=0;i<=2;++i)
    {
        x[k]=i;
        dfs(k+1);
    }
}
int main()
{
    dfs(1);
    scanf("%d",&t);
    int Case=0;
    while(t--)
    {
        scanf("%d%d%d%d", &a,&b,&c,&d);
        int x=calc(a,b,c,d);
        printf("Case #%d: ",++Case);
        if(visit[x]==0||(a>10||b>10||c>10||d>10)) puts("Wrong Scoreboard");
        else if(visit[x]==1) puts("Yes");
        else if(visit[x]>=2) puts("No");
    }
    return 0;
}

Sticks POJ - 1011( DFS搜索 )

链接

题意:给定 n 根小木棍,用它们组成相同长度的大木棍,问最短的相同长度是多少

思路: n 最多为50,规模较小

  • dfs(p,now,len,k)记录了位置 p、当前组成长度 now、一根大木棍需要的长度、剩余的小木棍数量
  • 两个剪枝:
    1、dfs之后, n o w = 0 now=0 now=0,意思是从 0 开始都不能使用这根木棍,肯定无法凑成
    2、dfs之后, n o w + a [ i ] = = l e n now+a[i]==len now+a[i]==len ,这根木棍都不能使用,那就一定不能组成
  • 注意回溯的时候,visit变量的记录变化。dfs之后,及时更新。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=100+10;

int n;
int a[maxn],visit[maxn];

bool dfs(int p,int now,int len,int k)
{
    if(k==0&&now==0)
        return 1;
    if(k<0) return 0;
    for(int i=p;i<=n;++i)
    {
        if(visit[i]) continue;
        if(now+a[i]==len)
        {
            visit[i]=1;
            if(dfs(1,0,len,k-1))
                return 1;
            else
            {
                visit[i]=0;
                return 0;
            }
        }
        if(now+a[i]<len)
        {
            visit[i]=1;
            if(dfs(p+1,now+a[i],len,k-1))
                return 1;
            visit[i]=0;
        }
        if(now==0)
            return 0;
    }
    return 0;
}

bool cmp(int a,int b)
{
    return a>b;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        ll sum=0;
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),sum+=a[i];
        sort(a+1,a+1+n,cmp);
        for(int i=a[1];i<=sum;++i)
        {
            if(sum%i!=0) continue;
            memset(visit,0,sizeof(visit));
            if(dfs(1,0,i,n))
            {
               printf("%d\n",i);
               break;
            }
        }
    }
    return 0;
}

Square POJ - 2362(数组 n 上做 0 、1选择)

DFS搜索习题_第6张图片
题意:给定 n 根木棍,问凑成一个正方形。
思路:和上题一样,需要记录位置 p,不然会超时。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=100+10;

int n;
int a[maxn],visit[maxn];

bool dfs(int p,int now,int len,int k)
{
    if(k==0&&now==0)
        return 1;
    if(k<0) return 0;
    for(int i=p;i<=n;++i)
    {
        if(visit[i]) continue;
        if(now+a[i]==len)
        {
            visit[i]=1;
            if(dfs(1,0,len,k-1))
                return 1;
            else
            {
                visit[i]=0;
                return 0;
            }
        }
        if(now+a[i]<len)
        {
            visit[i]=1;
            if(dfs(i+1,now+a[i],len,k-1))
                return 1;
            visit[i]=0;
        }
        if(now==0)
            return 0;
    }
    return 0;
}

bool cmp(int a,int b)
{
    return a>b;
}

int t;

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        ll sum=0;
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),sum+=a[i];
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;++i) visit[i]=0;
        if(sum%4||a[1]>sum/4)
        {
            puts("no");
            continue;
        }
        if(dfs(1,0,sum/4,n))
            puts("yes");
        else puts("no");
    }
    return 0;
}

Unit Fraction Partition POJ - 1980(DFS搜索)

链接

题意:给定 p、q、a、n,最多用n个单位分数相加组成 p q \frac pq qp,单位分数的乘积不能超过a,求不同的方案数。 ( n ≤ 7 ) (n\le 7) n7
思路: 数据规模比较小

  • dfs(x,p,q,pro,k,a)记录了当前枚举的单位分数 x,分子 p、分母 q、单位分数分母的乘积、最多使用 k 个。
  • 剪枝:当 p q > k x \frac pq > \frac kx qp>xk 的时候,k 个元素全部取最大都无法满足,那么直接返回。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
int ans;
void dfs(int x,int p,int q,int pro,int k,int a)
{
    if(p==0&&k>=0)
    {
        ans++;
        return;
    }
    if(p<0||k<0||k*q<p*x)
        return;
    for(int i=x;pro*i<=a;i++)
        dfs(i,p*i-q,q*i,pro*i,k-1,a);
}
int p,q,a,n;
int main()
{
    while(scanf("%d%d%d%d",&p,&q,&a,&n)&&(p||q||a||n))
    {
        ans=0;
        dfs(1,p,q,1,n,a);
        printf("%d\n",ans);
    }
    return 0;
}

Beat HDU - 2614( 二维数组上搜索)

DFS搜索习题_第7张图片
题意:当前解决的问题为 0,花费了 0 分钟。每次都会选择花费时间大于等于上次解决问题的时间,问最多能够解决多少个问题
思路

  • dfs(p,dif,tot)记录当前需要解决的问题 p,上一个问题的难度,已经解决的问题数
  • 细节:记得将问题 0标记为访问
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=100+10;

int n;
int t[20][20],ans;
int visit[20];

void dfs(int p,int dif,int tot)
{
    ans=max(ans,tot);
    for(int j=1;j<=n;++j)
    {
        if(!visit[j]&&t[p][j]>=dif)
        {
            visit[j]=1;
            dfs(j,t[p][j],tot+1);
            visit[j]=0;
        }
    }
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                scanf("%d",&t[i][j]);
        ans=1;
        memset(visit,0,sizeof(visit));
        visit[1]=1;
        dfs(1,0,1);
        printf("%d\n",ans);
    }
    return 0;
}

符号三角形 HDU - 2510 (数组 n 上做 0 、1选择)

DFS搜索习题_第8张图片

链接

思路:直接打表
打表程序

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=100+10;


int n;
int a[30][30],b[30];
int cnt[2],ans;

void dfs(int k)
{
    if(k==n+1)
    {
        cnt[0]=cnt[1]=0;
        for(int i=1;i<=n;++i) a[1][i]=b[i];
        for(int i=2;i<=n;++i)
            for(int j=1;j<=n-i+1;++j)
                a[i][j]=a[i-1][j]^a[i-1][j+1];
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n-i+1;++j)
                cnt[a[i][j]]++;
        if(cnt[0]==cnt[1])
            ans++;
        return;
    }
    b[k]=1;
    dfs(k+1);
    b[k]=0;
    dfs(k+1);
}

int main()
{
    for(n=1;n<=24;++n)
    {
        if(n*(n+1)%4)
        {
            printf("0,");
        }
        else
        {
            ans=0;
            dfs(1);
            printf("%d,",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(DFS搜索)