【题解】Luogu P4962 朋也与光玉
作者:互联网
其实是有点玄学在的
状压 \(DP\)
题意: 每个点一个颜色,找到一条最短的点数为 \(k\) 、恰好经过全部 \(k\) 种颜色的路径。你需要求出这条路径的长度。(\(k <= 13\))
思路: 看数据范围,妥妥的状压。考虑怎么定义状态。
首先,“点数为 \(k\) 、恰好经过全部 \(k\) 种颜色” 说明路径上的每个点颜色必须不同,那直接记录路径已经找到的颜色,再记录一个当前位置,就可以限制下一个点的走向了。
于是定义 \(dp[i][j]\) 为已找到的颜色序列为 \(i\),当前位置为 \(j\) 的最短路径。
\[dp[i|(1 << col[k])][k] = min(dp[i][j] + val, dp[i|(1 << col[k])][k]) \]然后,就是直接来了。枚举当前状态和位置,枚举下一个可以到达的位置,转移状态即可。
感觉上是一个纯纯的暴力,带点优化。但是不开o2也能过()
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const int N = 105, M = (1 << 13) + 5;
int n, m, k, col[N];
ll ans = 1e15, dp[M][N];
int tot, head[N], to[N * N], nex[N * N], w[N * N];
void add(int x, int y, int z) {
to[++tot] = y, nex[tot] = head[x], head[x] = tot, w[tot] = z;
}
signed main() {
memset(head, -1, sizeof(head));
memset(dp, 0x3f, sizeof(dp));
scanf("%d %d %d", &n, &m, &k);
for(int i = 0; i < 1 << k; i ++) {
for(int j = 1; j <= n; j ++) dp[i][j] = 1e15;
}
for(int i = 1; i <= n; i ++) {
scanf("%d", &col[i]);
dp[1 << col[i]][i] = 0;
}
int u, v, ww;
for(int i = 1; i <= m; i ++) {
scanf("%d %d %d", &u, &v, &ww);
add(u, v, ww);
}
for(int i = 0; i < (1 << k); i ++) {
for(int j = 1; j <= n; j ++) {
if(!(i & (1 << col[j]))) continue;
for(int t = head[j]; t != -1; t = nex[t]) {
if(i & (1 << col[u = to[t]])) continue;
v = i | (1 << col[u]);
dp[v][u] = min(dp[v][u], dp[i][j] + w[t]);
}
}
}
for(int i = 1; i <= n; i ++) ans = min(ans, dp[(1 << k) - 1][i]);
if(ans == 1e15) puts("Ushio!");
else printf("%lld\n", ans);
return 0;
}
标签:状压,颜色,题解,路径,P4962,long,include,朋也,dp 来源: https://www.cnblogs.com/Spring-Araki/p/16480939.html