状压dp求解tsp问题

郊区旅游

在这里插入图片描述
AC代码:

#include <bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;
int n, m, r;
int ar[20];
int mp[205][205];
int dp[1<<16][20];
int x, y, z;

void floyd()
{
    for(int k = 1; k <= n; ++k)
    {
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= n; ++j)
                mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &r);
    for(int i = 1; i <= r; ++i) scanf("%d", &ar[i]);

    memset(mp, 0x3f, sizeof(mp));
    memset(dp, 0x3f, sizeof(dp));

    for(int i = 1; i <= n; ++i) mp[i][i] = 0;

    while(m--)
    {
        scanf("%d%d%d", &x, &y, &z);
        mp[x][y] = mp[y][x] = z;
    }

    floyd();

    for(int i = 1; i <= r; ++i) dp[1<<(i-1)][i] = 0;

    for(int s = 0; s < (1<<r) - 1; ++s)
    {
        for(int i = 1; i <= r; ++i)
        {
            if(!(s >> (i - 1) & 1)) continue;
            for(int j = 1; j <= r; ++j)
            {
                if(s >> (j - 1) & 1) continue;
                if(mp[ar[i]][ar[j]] != inf)
                {
                    dp[s|(1<<(j-1))][j] = min(dp[s][i] + mp[ar[i]][ar[j]], dp[s|(1<<(j-1))][j]);
                }
            }
        }
    }

    int ans = 0x3f3f3f3f;

    for(int i = 1; i <= r; ++i)
    {
        ans = min(ans, dp[(1<<r) - 1][i]);
    }

    printf("%d\n", ans);
    return 0;
}

简单环

在这里插入图片描述
AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll mod = 998244353;
int n, m, k;
bool mp[25][25];
int x, y;
ll dp[1100000][25]; // 2^20 = 1048576
ll ans[30];
int mi, len;

ll pow_mod(ll a, ll n)
{
    ll ans = 1;
    a %= mod;
    while(n)
    {
        if(n & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
        n >>= 1;
    }
    return ans;
}

int get_len(int x)
{
    int res = 0;
    while(x)
    {
        if(x&1) res++;
        x /= 2;
    }
    return res;
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d%d", &x, &y);
        mp[x][y] = mp[y][x] = true;
    }

    for(int i = 1;i <= n; ++i)
    {
        dp[1<<(i-1)][i] = 1;
    }



    for(int s = 0; s < (1<<n); ++s) //枚举每一种状态
    {
        for(int i = 1; i <= n; ++i) //找到该种状态的最小点,用来标识路径
        {
            if((s>>(i-1))&1)
            {
                mi = i;
                break;
            }
        }

        len = get_len(s);

        for(int i = mi; i <= n; ++i) //枚举对于当前路径s时,现在要由哪一个点向外再次延长路径
        {
            if(!(s >> (i-1)&1)) continue; //这个点如果不在路径中则跳过
            for(int j = mi; j <= n; ++j) //枚举下一个点,确定了当前st是最小的点 那么就不要再去枚举比st小的点了 否则重复计算了
            {
                if(!mp[i][j]) continue; //于下一个点不连通
                if(mi == j) //下一个点是起点,形成环,记录答案
                {
                    if(len >= 3)
                        ans[len%k] = (ans[len%k] + dp[s][i]) % mod;
                }
                if(s >> (j - 1) & 1) continue; //这个点已经在路径里了
                int nxt = s | 1 << (j - 1);
                dp[nxt][j] = (dp[nxt][j] + dp[s][i]) % mod;
            }
        }
    }

    ll sum = 0;

    for(int i = 0; i < k; ++i)
    {
        printf("%lld\n", (ans[i] * pow_mod(2, mod - 2)) % mod);
    }

    //printf("%lld\n", (ans - m) / 2);
    //cout << (ans - m) / 2 << '\n';

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值