惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Last Week in AI
Last Week in AI
月光博客
月光博客
D
DataBreaches.Net
WordPress大学
WordPress大学
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
C
Check Point Blog
F
Fortinet All Blogs
B
Blog
小众软件
小众软件
Vercel News
Vercel News
罗磊的独立博客
有赞技术团队
有赞技术团队

博客园 - katago

【最大子矩形】极大化思想 MobaXterm-Keygen The "vmwgfx unsupported hypervisor" error in VirtualBox with Ubuntu codex 安装必备环境 chatgpt提示词 通过VScode的远程连接 WSL,配置Linux平台python开发环境 扫地机器人基本设计方案 wsl2 安装 SRS + FFmpeg 直播机 拉流推给本机 SRS 的 RTMP P1171 售货员的难题 for循环状压dp写法 leetcode [698] 划分为k个相等的子集 LeetCode 473. 火柴拼正方形 vscode LeetCode插件安装 LeetCode 464. 我能赢吗 P1825 [USACO11OPEN] Corn Maze S 如何找代码bug 八中 搜索作业 线性筛素数计数 atcoder dp基础 八中上机课练习题单 整除分块
P1171 售货员的难题 dfs 状态压缩 + 记忆化搜索
katago · 2026-01-08 · via 博客园 - katago

运行时间最长一个测试用例跑646ms,比for循环430ms慢一些

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n, g[N][N], dp[1<<N][N];
int ALL;
// 状态压缩 + 记忆化搜索
// s: 当前访问过的点的集合
// u: 当前所在的点
int dfs(int s, int u){
    if(s == ALL){// 所有点都访问过
        return g[u][0];// 回到起点
    }
    if(dp[s][u] != -1) return dp[s][u];
    int ans = INT_MAX;
    for(int v=0;v<n;v++){
        if((s>>v)&1) continue;
        ans = min(ans, g[u][v]+dfs(s|(1<<v), v));
    }
    dp[s][u] = ans;
    return ans;
}
int main()
{
    cin>>n;
    ALL=(1<<n) - 1;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>g[i][j];
    memset(dp, -1, sizeof dp);
    cout << dfs(1, 0);// 从点 0 出发,点 0 已访问
    return 0;
}

用 __builtin_ctz 优化,只枚举集合中为 1 的位
运行时间最长一个测试用例跑513ms,快赶上for循环dp了

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n, g[N][N], dp[1<<N][N];
int ALL;
int dfs(int s, int u){
    if(s == ALL){// 所有点都访问过
        return g[u][0];// 回到起点
    }
    if(dp[s][u] != -1) return dp[s][u];
    int ans = INT_MAX;
    // for(int v=0;v<n;v++){
    //     if((s>>v)&1) continue;
    //     ans = min(ans, g[u][v]+dfs(s|(1<<v), v));
    // }
    
    int rest = ALL ^ s;// 剩余未访问的点
    // 只枚举集合中为 1 的位
    while(rest){
        int v = __builtin_ctz(rest);//取最低位 1 的下标
        rest &= rest - 1;//去掉最低位的1
        ans = min(ans, g[u][v] + dfs(s|(1<<v), v));
    }
    dp[s][u] = ans;
    return ans;
}
int main()
{
    cin>>n;
    ALL=(1<<n) - 1;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>g[i][j];
    memset(dp, -1, sizeof dp);
    cout << dfs(1, 0);// 从点 0 出发,点 0 已访问
    return 0;
}