lzm的随笔

个人随笔


  • 首页

  • 归档

  • 搜索

2026年10月19日杂谈

时间: 2026-10-19 分类: 训练日记   字数: 1240 字 阅读: 3分钟 阅读次数:

复建了一些水题,主要是深入浅出进阶篇的一些东西。我突然发现高中那会就没怎么练过搜索,上大学这样可不行!

水题,但是不影响我糖

选数 - 洛谷

我们注意到数据范围只有 $20$, 数据范围很小,即使采取暴力dfs估计也没事,接下来第一个问题就是考虑怎么判断一个数是不是素数。

简单看了一下数据范围,采取如下方法即可,时间复杂度 $O(\sqrt{n})$

inline bool ispri(int x)
{
    if(x==1)
    {
        return false;
    }
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}

接下来通过dfs实现,如果找到了 $k$ 个数,这样就可以直接判断是否是素数,如果是累计答案。如果没找全 $k$ 个数字,继续进行dfs即可。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
inline int read(void)
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        {
           f=-1;
        }
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
constexpr int maxn = 25;
int a[maxn];
inline bool ispri(int x)
{
    if(x==1)
    {
        return false;
    }
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}
int ans,n,k;
inline void dfs(int x,int cnt,int sum)
{
    if(cnt==k+1)
    {
        if(ispri(sum))
        {
            ans++;
        }
        return;
    }
    for(int i=x;i<=n;i++)
    {
        dfs(i+1,cnt+1,sum+a[i]);
    }
}
int main(void)
{
    n=read(),k=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
    }
    dfs(1,1,0);
    cout<<ans<<endl;
    return 0;
}

P1433 吃奶酪 - 洛谷

先说 $90$ 分做法,观察到数据范围很小,考虑直接dfs+剪枝优化。从老鼠所在的位置出发,挨个枚举奶酪,如果没被吃直接吃掉,然后在此状态下继续进行dfs。如果当前的方案明显不优(即超过了已有的最小路程)直接return即可,体现剪枝的思想。如果都吃完了,而且目前的路径明显更优,这样就可以统计答案了。

通常在深度搜索算法中,最应该注意的就是参数的设置,在此题目中包括:

出发的位置

已经吃了多少奶酪

已经走了多远

这样我们不难看出走到下一个点的时候参数如何迭代,但是我们要注意一件事情,依题意这道题没有回头路一说,我们可以用一个布尔型数组vis维护某个点是否被访问到。值得一提的是,我们要从老鼠当前所在的位置出发,去枚举每一个奶酪,即从哪个奶酪开始吃,这样类似于“一条路走到黑”,也就是直接计算从第 $i$ 个奶酪开始吃有没有合法方案。因此我们需要在回溯执行完毕之后恢复vis[i]=0,这样可以保持好后续的dfs操作不受影响。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
inline int read(void)
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        {
           f=-1;
        }
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
constexpr int maxn = 20;
struct cheese
{
    double x,y;
}a[maxn];
int n;
double dis[maxn][maxn];
inline double getDis(cheese a,cheese b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init(void)
{
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            dis[i][j]=getDis(a[i],a[j]);
        }
    }
}
double ans=0x3f3f3f3f;
bool vis[maxn];
inline void dfs(int cur,int sum,double len)
{
    if(len>=ans)
    {
        return;
    }
    if(sum==n)
    {
        ans=min(len,ans);
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            // continue;
            vis[i]=1;
            dfs(i,sum+1,len+dis[cur][i]);
            vis[i]=0;
        }
        // vis[i]=1;
        
        // vis[i]=0;
    }
}
int main(void)
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].x>>a[i].y;
    }
    init();
    a[0].x=0;
    a[0].y=0;
    dfs(0,0,0);
    cout<<fixed<<setprecision(2)<<ans<<endl;
    return 0;
}

然后你非常漂亮的 $TLE $ 了三个点……

主要还是因为常数大导致的,我们考虑状态压缩,我们知道,计算机中的一个数是由无数个 $0$ 和 $1$ 组成的,假设我们有 $5$ 块奶酪,可以用 $(11000)_2$ 表示前两块被吃了后三块没有。这样我们可以用 f[i][j] 表示在 i 状态(二进制)下,老鼠位于j点时走过的最短路程。那么这样写有什么优点呢?这样写相当于把所需要的数据提前预处理出来了,一定程度上节省了所需时间,就像库函数一样“踩在巨人的肩膀上摘苹果”。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
inline int read(void)
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        {
           f=-1;
        }
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
constexpr int maxn = 20;
struct cheese
{
    double x,y;
}a[maxn];
int n;
double dis[maxn][maxn];
inline double getDis(cheese a,cheese b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init(void)
{
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            dis[i][j]=getDis(a[i],a[j]);
        }
    }
}
double ans=0x3f3f3f3f;
bool vis[maxn];
double f[1<<maxn][maxn];
inline void dfs(int cur,int sum,double len,int status)
{
    if(len>=ans)
    {
        return;
    }
    if(sum==n)
    {
        ans=min(len,ans);
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            // continue;
            // vis[i]=1;
            // dfs(i,sum+1,len+dis[cur][i]);
            // vis[i]=0;
            int nxtStatus = status+(1<<(i-1));
            if(f[nxtStatus][i] and f[nxtStatus][i]<=len+dis[cur][i])
            {
                continue;
            }
            vis[i]=true;
            f[nxtStatus][i]=len+dis[cur][i];
            dfs(i,sum+1,len+dis[cur][i],nxtStatus);
            vis[i]=false;
        }
        // vis[i]=1;
        
        // vis[i]=0;
    }
}
int main(void)
{
    // freopen("P1433_10.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].x>>a[i].y;
    }
    init();
    a[0].x=0;
    a[0].y=0;
    // dfs(0,0,0);
    dfs(0,0,0,0);
    cout<<fixed<<setprecision(2)<<ans<<endl;
    return 0;
}
QQ扫一扫交流

标题:2026年10月19日杂谈

链接:https://www.usamihearn.cn/post/2026%E5%B9%B410%E6%9C%8819%E6%97%A5%E6%9D%82%E8%B0%88/

作者:lzm

声明: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0许可协议,转载请注明出处!

创作实属不易,如有帮助,那就打赏博主些许茶钱吧 ^_^
WeChat Pay

微信打赏

Alipay

支付宝打赏

lzm

lzm

08软件工程本科在读,常驻哈尔滨 || 大连,感谢大家的支持

1 日志
1 分类
0 标签
GitHub
标签云
    © 2026 - 2026 lzm的随笔
    Powered by - Hugo v0.168.0 / Theme by - NexT
    Storage by /
    0%