Gym-101908 J. Joining Capitals, 洛谷P6192 (最小斯坦纳树)
Joining Capitals题目大意:
给你n个点k个特殊点他们的坐标,求包含k个特殊点的点集的最小代价
且k个特殊点必须作为叶子节点
代价的计算为两点的欧几里德距离
题目思路:
我们用dp[state][i]表示i为根时,选取的特殊点的状态为state时的最小代价
第一类转移我们可以直接枚举子集s1,s2
dp[state][i] = max(dp[state][i],dp[s1][i]+dp[s2][i])
这里用到了二进制下枚举子集的一种方法,所以不必暴力枚举
第二类转移我们枚举要拓展的点new,注意这里new是要作为根节点的,但特殊点只能做叶子节点
所以在转移时要加限定条件new>m
因为距离(代价)可以直接用欧几里德距离求得,所以这里不必再写最短路算法
CODE:
struct Node { double x, y; } a[maxn]; int n, m; double dp[1 << 12][200], dis[200][200]; double DIS(int x, int y) { return sqrt((a[x].x - a[y].x) * (a[x].x - a[y].x) + (a[x].y - a[y].y) * (a[x].y - a[y].y)); } int main() { n = read(), m = read(); for(int i = 1 ; i <= n ; i++) cin >> a[i].x >> a[i].y; rep(i, 0, 1 << 11) rep(j, 0, n) dp[i][j] = 100000000; rep(i, 1, m) dp[1 << (i - 1)][i] = 0; rep(i, 1, n) rep(j, 1, n)dis[i][j] = DIS(i, j); for(int i = 1 ; i < (1 << m) ; i++) { for(int j = (i - 1)&i ; j ; j = (j - 1)&i) for(int k = 1 ; k <= n ; k++) dp[i][k] = min(dp[i][k], dp[j][k] + dp[i ^ j][k]); for(int j=1 ;j<=n ;j++) for(int k=1 ;k<=n ;k++) if(k>m)dp[i][k] = min(dp[i][k],dp[i][j]+dis[k][j]); } double ans = 100000000; rep(i,1+m,n)if(dp[(1<<m)-1][i]<ans) ans = dp[(1<<m)-1][i]; printf("%.5lf\n",ans ); return 0 ; }
洛谷P6192最小斯坦纳树 模板题
不过在进行第二类转移的时候要加最短路算法进行转移
个人认为这里最短路算法是用来优化转移的
拿这个题来说,我们完全可以Flody预处理出所有点的距离之后采用跟上面的题一样的n^2的转移
但是如果用spfa或者dijkstra来转移的话,在面对一些数据时肯能效率会更高
CODE_flody:
int n, m, k, head[maxn], cnt; struct node { int u, v, w, next; } e[maxn]; void add(int u, int v, int w) { e[cnt].u = u, e[cnt].v = v, e[cnt].w = w; e[cnt].next = head[u], head[u] = cnt++; } int t[maxn]; int dp[1 << 12][102]; int dis[102][102]; void Flody() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); } } } } int main() { n = read(), m = read(), k = read(); mst(head, -1), mst(dis, inf); rep(i, 1, n) dis[i][i] = 0; for (int i = 1; i <= m; i++) { int u, v, w; u = read(), v = read(), w = read(); dis[v][u] = dis[u][v] = min(dis[u][v], w); add(u, v, w), add(v, u, w); } Flody(); rep(i, 1, k) t[i] = read(); rep(i, 0, 1 << 11) rep(j, 0, 101) dp[i][j] = inf; rep(i, 1, k) dp[1 << (i - 1)][t[i]] = 0; for (int i = 1; i < (1 << k); i++) { for (int j = i; j; j = (j - 1) & i) { for (int k = 1; k <= n; k++) { dp[i][k] = min(dp[i][k], dp[j][k] + dp[j ^ i][k]); } } for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { dp[i][k] = min(dp[i][k], dp[i][j] + dis[j][k]); } } } out(dp[(1 << k) - 1][t[1]]); return 0; }
CODE_spfa:
int n, m, k, head[maxn], cnt; struct node { int u, v, w, next; } e[maxn]; void add(int u, int v, int w) { e[cnt].u = u, e[cnt].v = v, e[cnt].w = w; e[cnt].next = head[u], head[u] = cnt++; } int t[maxn]; int dp[1 << 12][102]; queue<int> q; int vis[maxn]; void spfa(int S) { while (q.size()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].v; if (dp[S][v] > dp[S][u] + e[i].w) { dp[S][v] = dp[S][u] + e[i].w; if (vis[v] == 0) { vis[v] = 1; q.push(v); } } } } } int main() { n = read(), m = read(), k = read(); mst(head, -1); for (int i = 1; i <= m; i++) { int u, v, w; u = read(), v = read(), w = read(); add(u, v, w), add(v, u, w); } rep(i, 1, k) t[i] = read(); rep(i, 0, 1 << 11) rep(j, 0, 101) dp[i][j] = inf; rep(i, 1, k) dp[1 << (i - 1)][t[i]] = 0; for (int i = 1; i < (1 << k); i++) { for (int j = i; j; j = (j - 1) & i) { for (int k = 1; k <= n; k++) { dp[i][k] = min(dp[i][k], dp[j][k] + dp[j ^ i][k]); if (dp[i][k] != inf) q.push(k), vis[k] = 1; } } spfa(i); } out(dp[(1 << k) - 1][t[1]]); return 0; }