P4962 朋也与光玉

一道可能不太明显的状压dp

为什么说不明显呢?因为可以状压的元素藏到了后面。

题意已经很清晰了:每个点一个颜色,找到一条最短的点数为\(k\)、恰好经过全部\(k\)种颜色的路径。你需要求出这条路径的长度。

虽然\(n \leq 100\)挺大的,但是\(k \leq 13\),而我们要走的也只跟\(k\)有关,所以我们需要压缩\(k\)

dp[i][j]为当前走到下标为\(i\)的节点,走了颜色的状态为\(j\)的最短路径。

初始化挺简单的,就不讲了。

转移有点东西,看上去挺不可过的,但还是能过的。

首先枚举开辟颜色的当前状态,取出其中一个颜色,找到是这个颜色的节点,以她作为起点,再找当前没开辟过的颜色,找到所有是这个颜色的节点,对他们进行开辟。

可能不用判没路的情况,但我还是判了。

这样按照思路写一写,就可以过了!

所以数据可能有点水咯。。。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
const int maxn = 105;
const int INF = 0x3f3f3f3f;
int G[maxn][maxn];
int dp[maxn][8200];// 目前走到i,取k个点的状态为j的最短路
int w[maxn];
int n, m, k;
std::vector<int> vec[14];// 我用vector存的
int main()
{
    memset(G, 0x3f, sizeof G);
    memset(dp, 0x3f, sizeof dp);
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &w[i]); 
        G[i][i] = 0;
        vec[w[i]].push_back(i);
    }
    while(m--)
    {
        int u, v, w; scanf("%d%d%d", &u, &v, &w);
        G[u][v] = std::min(G[u][v], w);
    }
    for(int i = 1; i <= n; i++)
    {
        dp[i][1 << w[i]] = 0;
    }
    int S = (1 << k) - 1;
    for(int i = 1; i <= S; i++)// 状态
    {
        for(int j = 0; j < k; j++)
        {
            if((1 << j) & i)
            {
                int size1 = vec[j].size();
                for(int l = 0; l < size1; l++)
                {
                    int u = vec[j][l];// 前节点
                    for(int o = 0; o < k; o++)
                    {
                        if((1 << o) & i) continue;
                        int size2 = vec[o].size();
                        for(int p = 0; p < size2; p++)
                        {
                            int v = vec[o][p];
                            if(G[u][v] != INF)
                            {
                                dp[v][i | (1 << o)] = std::min(dp[v][i | (1 << o)], dp[u][i] + G[u][v]);
                            }
                        }
                    }
                }
            }
        }
    }
    int ans = INF;
    for(int i = 1; i <= n; i++) ans = std::min(ans, dp[i][S]);
    if(ans == INF) printf("Ushio!\n");
    else printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Garen-Wang/p/9919139.html
今日推荐