【周总结】暑假1

本文深入探讨了深度优先搜索(DFS)在解决编程竞赛问题中的应用,包括成语接龙、数的变换、波浪数、数字序列等。通过分析题目和解题思路,展示了深搜在不同问题中的解决策略,如检查接口可行性、计算数字变换次数、寻找双重波浪数等。同时,文章还介绍了其他相关算法思想,如暴力枚举、杨辉三角等。
摘要由CSDN通过智能技术生成

深搜

P1019 NOIP2000 提高组单词接龙

题意:成语接龙,规定开头字母,字符串接龙找最长,每个单词最多使用两次

解题思路:搜索

需要一个数组记录单词次数,一个单词最多两次,visit数组
check函数,是在枚举接口长度的时候判断接口长度的可行性
add函数,简单的连接函数,注意引用
最重要的是深搜,dfs函数

#include <bits/stdc++.h>

using namespace std;
/*
肯定有不同的方案,但是还需要将重叠的部分去掉不计算
还需要记录次数
*/
const int N=100;
int n;
int ans=0;
string word[N];//存储单词
string inti;//原始
int visit[N];//用来记录dfs时候每个单词被使用了几次数组
//k应该是之前方案的最优解
bool check(string s,string m,int k)//check函数判断接口的可行性
{//把m拼接到s上,k为接口长度
    int l=s.length();
    for(int i=0;i<k;i++)
    {
        if(s[l-k+i]!=m[i]) return false;
    }
    return true;
}
void add(string &s,string m,int k)//因为是要吧m接到s上,所以引用传参
{
    int ll=m.length();
    for(int i=k;i<ll;i++)
    {
        s+=m[i];//k是最小拼接长度的位置
    }
}
void dfs(string now)
{
    int x=now.length();
    ans=max(ans,x);//拼接之后更新长度,ans初始为0
    for(int i=1;i<=n;i++)
    {
        if(visit[i]>=2)//为什么这个判断在这?
            continue;
        int maxk = word[i].length();
        for(int j=1;j<=maxk;j++)//枚举接口长度,其实这一个过程也在不断地更新
        {
            if(check(now,word[i],j))
            {
                string temp=now;//存一个原版为了拼接
                add(temp,word[i],j);
                if(temp==now)//如果是原串就没有意义不进行接下来的操作
                    continue;
                visit[i]++;
                dfs(temp);
                visit[i]--;//回溯???看似平淡无奇?!
            }
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>word[i];
    }
    cin>>inti;
    dfs(inti);
    cout<<ans<<endl;
    return 0;
}

P1037 NOIP2002 普及组 产生数

暴力

#include <bits/stdc++.h>
#define long long ll

using namespace std;
//包括原数 能变化出多少种数
//一个数能变成的次数相乘起来就是一个整数能变成的次数
int n,k;
string s;
int v[10][10];//顶多0~9
int f[10];
int num[101];
void floyd()
{
    for(int k=0; k<=9; k++)
        for(int i=0; i<=9; i++)
            for(int j=0; j<=9; j++)
                v[i][j]=v[i][j]||(v[i][k]&&v[k][j]);
}

int main()
{
    cin>>s>>k;
    while(k--)
    {
        int a,b;
        cin>>a>>b;
        v[a][b]=true;//a变成b

    }
    for(int i=0; i<=9; i++) v[i][i]=true ; //当然自己可以变成自己
    floyd();
    for(int i=0; i<=9; i++)
        for(int j=0; j<=9; j++)
            if(v[i][j]) f[i]++;//求出i可以变成的数字
    int len=2;
    num[1]=1;
    for(int i=0; i<s.length(); i++) //高精度
    {
        for(int j=1; j<=100; j++)
            num[j]*=f[s[i]-'0'];
        for(int j=1; j<=100; j++) //每一位都要相乘
            if(num[j]>=10) //进位
            {
                num[j+1]+=num[j]/10;
                num[j]%=10;
            }
        while(num[len]!=0) len++;//求出长度
    }
    for(int i=len-1; i>=1; i--) cout<<num[i]; //输出;
    cout<<endl;
    return 0;
}

P1112 波浪数

题意理解补充:所谓双重数,是在两种进制下,都是波浪数。
例如,十进制数191919191919是一个十进制下的波浪数,它对应的十一进制数121212121212也是一个波浪数,所以十进制数191919191919是一个双重波浪数。
那么三重,四重波浪数类似。

#include <bits/stdc++.h>

using namespace std;
//在指定范围内找到双重,三重,四重波浪数
//搜索进制构造数论数学
const int N=10000005;
//哈希
int v[N];
int a,b,l,r,c,t,x;
//直接暴力不知道能不能过了
int main()
{
    cin>>a>>b>>l>>r>>c;
    for(int k=a; k<=b; k++)//k是代表进制
        for(int i=1; i<k; i++)
            for(int j=0; j<k; j++)
            {
                if(i!=j)//不能构成相同的波浪数字
                {
                    x=0;
                    t=0;
                    while(x<=r)
                    {
                        if(t%2==0)//如果是偶数位置就加i
                        {
                            x=x*k+i;
                            t++;
                        }else{//奇数位置
                            x=x*k+j;
                            t++;
                        }
                        if(x>=l&&x<=r) v[x]++;
                        //cout<<v[x]<<"v[x] "<<x<<endl;
                    }
                }
            }
            for(int i=l;i<=r;i++){
                if(v[i]==c) cout<<i<<endl;
            }
}

P1118 USACO06FEBBackward Digit Sums G/S

典型深搜思想

//有很多种情况输出字典序最小的那种情况
//1到n的排列,字典序
//暴力的方法当然是按字典序枚举最开始的序列,将每个排列都计算枚举得到最后的
//如果这个值和题目中的sum相等就找到了答案
//如何按照字典序枚举,可以使用排序函数,也可以在dfs枚举排列时,每一层都从小到大枚举这样自然就是按照字典序枚举了
/*
举例子做,各项系数恰好与杨辉三角有关系
c[1][1]=1;
for(int i=2;i<=n;i++)//由于这里数组的记录是从1开始的,所以不用担心越界
for(int j=1;j<=i;j++)
c[i][j]=c[i-1][j]+c[][];//每个数都等于它肩上的两个数的和
*/

#include <bits/stdc++.h>

using namespace std;
//有很多种情况输出字典序最小的那种情况
//1到n的排列,字典序
//暴力的方法当然是按字典序枚举最开始的序列,将每个排列都计算枚举得到最后的
//如果这个值和题目中的sum相等就找到了答案
//如何按照字典序枚举,可以使用排序函数,也可以在dfs枚举排列时,每一层都从小到大枚举这样自然就是按照字典序枚举了
/*
举例子做,各项系数恰好与杨辉三角有关系
c[1][1]=1;
for(int i=2;i<=n;i++)//由于这里数组的记录是从1开始的,所以不用担心越界
    for(int j=1;j<=i;j++)
    c[i][j]=c[i-1][j]+c[][];//每个数都等于它肩上的两个数的和
*/
int n,p;
int a[13];//输出
int c[13][13];//杨辉三角
bool b[13];//判重必备
void dfs(int dep,int s)
{
    if(s>p) //如果累加的数已经超过给定的数就返回
    {
        return ;
    }
    if(dep>n)
    {
        if(s==p) //如果答案跟给定的数相等
        {
            cout<<a[1];
            for(int i=2; i<=n; i++)
            {
                cout<<" "<<a[i];
            }
            exit(0);//进程的终止
        }
        return ;//如果没有输出答案就返回
    }
    for(int i=1; i<=n; i++)
    {
        if(b[i]==false)//如果当前这个数没有用过
        {
            b[i]=true;//标记为用过
            a[dep]=i;//保存第dep个取的数
            dfs(dep+1,s+i*c[n][dep]);
            b[i]=false;//状态回归
        }
    }
}
int main()
{
    cin>>n>>p;
    c[1][1]=1;//初始化杨辉三角
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=i; j++)
        {
            c[i][j]=c[i-1][j]+c[i-1][j-1];//生成杨辉三角
        }
    }
    dfs(1,0);//深搜
    return 0;
}

P1123 取数游戏

#include <bits/stdc++.h>
//取数字任意两个不相邻使其和相加最大
//因为会影响到下一步的选择所以排除了贪心和动态规划
//这个题其实不会做!!!!!
//感觉这个 别人写的搜索有些奇怪
int n,m;
using namespace std;
const int N=21;
int d[8][2]={1,0,-1,0,0,1,0,-1,1,1,-1,1,1,-1,-1,-1};//方向数组用来控制搜索的方向
int t,s[8][8],mark[8][8],ans,mx;
void dfs(int x,int y)//搜索函数,表示搜索点(x,y)
{
    if(y==m+1)//继续搜索下一行
    {
        dfs(x+1,1);
    return ;
    }
    if(x==n+1){//当边界时,搜索就结束了
        mx=max(ans,mx);//更新最大值
        return ;
    }
    dfs(x,y+1);//不去次数的情况
    if(mark[x][y]==0)//取次数时,需保证次数周围没有 其他数
    {
        ans+=s[x][y];
        for(int fx=0;fx<8;++fx){//标记周围的数字
            ++mark[x+d[fx][0]][y+d[fx][1]];
        }
        dfs(x,y+1);
        for(int fx=0;fx<8;fx++)//回溯
        {
            --mark[x+d[fx][0]][y+d[fx][1]];
        }
        ans-=s[x][y];
    }
}
int main()
{

    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        memset(s,0,sizeof(s));
        memset(mark,0,sizeof(mark));
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                cin>>s[i][j];
        mx=0;
        dfs(1,1);//从1 1开始搜索
        cout<<mx<<endl;

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值