P1171 售货员的难题 dfs 状态压缩 + 记忆化搜索

运行时间最长一个测试用例跑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;
}

posted @ 2026-01-08 15:10  katago  阅读(21)  评论(0)    收藏  举报