2019 ICPC 南昌邀请赛 A-Attack(斯坦纳树)
题意:给出一个无向图,和四对数据。每对数据分别为图中的两个点。要求添加一些边,使每对点都能连通,让总边权最小。
分析:POJ3123、NWERC2006原题,《acm国际大学生程序设计竞赛:题目与解读》P564。斯坦纳森林模板题,把所有的集合放在一起,做一遍斯坦纳树,然后对所有集合做一遍子集合并dp。斯坦纳树的转移方程有两重:第一重,先通过连通状态的子集进行转移。dp[i][state]=min{ dp[i][subset1]+dp[i][subset2] } ,枚举子集的技巧可以用 for(sub=(state-1)&state;sub;sub=(sub-1)&state)。第二重,在当前枚举的连通状态下,对该连通状态进行松弛操作。dp[i][state]=min{ dp[i][state], dp[j][state]+e[i][j] }。复杂度 O(n*3^k+c*E*2^k)。k为要求连通的点数,c为SPFA复杂度中的常数,E为边的数量,但几乎达不到全部边的数量,甚至非常小。3^k来自于子集的转移,sum{C(i,n)*2^i} (1<=i<=n),用二项式展开求一下和。
斯坦纳树入门参考https://www.cnblogs.com/ECJTUACM-873284962/p/7643445.html#autoid-0-1-0。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;int n, m, cnt, u, v, w;
int status;///表示0~n号节点都被选择时的状态+1
int s[40], nt[2009], e[2009], val[2009];
int dis[40][1000], vis[40], ans[1000], flag[1000];
///dis[i][j]表示以i节点为根选择点集状态为j时的最小值;vis[i][j]表示i节点为点集j时是否在队列中
queue<int>q;
map<string, int>mp;/// 城市名和节点编号的对应关系
char s1[30], s2[30];///初始化
void init()
{memset(vis, 0, sizeof vis);memset(s, -1, sizeof s);mp.clear();cnt = 0;status = 1 << 8;for (int i = 1; i <= n; i++)for (int j = 0; j < status; j++)dis[i][j] = INF;
}///SPFA进行松弛
void SPFA(int sta)
{while (!q.empty()){int pre = q.front();q.pop();vis[pre] = 0;for (int i = s[pre]; ~i; i = nt[i]){if (dis[pre][sta] + val[i] < dis[e[i]][sta]){dis[e[i]][sta] = dis[pre][sta] + val[i];if (!vis[e[i]]){vis[e[i]] = 1;q.push(e[i]);}}}}
}
/// 斯坦纳树核心代码(状压dp)
void Steiner_Tree()
{for (int i = 0; i < status; i++){/// 第一重,先通过连通状态的子集进行转移。/// dp[i][state]=min{ dp[i][subset1]+dp[i][subset2] }for (int j = 1; j <= n; j++){for (int k = i; k; k = (k - 1) & i)dis[j][i] = min(dis[j][i], dis[j][k] + dis[j][i - k]);if (dis[j][i] != INF){q.push(j);vis[j] = 1;}}/// 第二重,在当前枚举的连通状态下,对该连通状态进行松弛操作。/// dp[i][state]=min{ dp[i][state], dp[j][state]+e[i][j] }SPFA(i);}
}/// 检查点对是否在k状态下的存在性是否一样 存在性不一致时返回真需要进行子集合并
int check(int k)
{for (int i = 0; i < 4; i++)if ((k >> i & 1) ^ (k >> (i + 4) & 1)) return 0;return 1;
}/// (斯坦纳森林解法)把所有的集合放在一起,做一遍斯坦纳树,然后对所有集合做一遍子集合并dp。
int main()
{while (~scanf("%d %d", &n, &m) && (n + m)){init();for (int i = 1; i <= n; i++) scanf("%s", s1), mp[s1] = i;for (int i = 1; i <= m; i++){scanf("%s%s%d", s1, s2, &w);u = mp[s1], v = mp[s2];nt[cnt] = s[u], s[u] = cnt, e[cnt] = v, val[cnt++] = w;nt[cnt] = s[v], s[v] = cnt, e[cnt] = u, val[cnt++] = w;}for (int i = 1; i <= 8; i++) ///处理四对点对{scanf("%s", s1);int temp = ((i & 1) ? i / 2 : i / 2 + 3); /// 0-4 1-5 2-6 3-7 一一对应dis[mp[s1]][1 << temp] = 0;}Steiner_Tree();/// 求出8个点(四个点对)都相连的花费for (int i = 0; i < status; i++){flag[i] = check(i), ans[i] = INF;for (int j = 1; j <= n; j++) ans[i] = min(ans[i], dis[j][i]);}///合并子集for (int i = 0; i < status; i++)if (flag[i])///需要合并时{///枚举子集转移for (int j = i; j; j = (j - 1)&i)if (flag[j] && flag[i - j]) ///转移条件ans[i] = min(ans[i], ans[j] + ans[i - j]);}printf("%d\n", ans[status - 1]);}return 0;
}
2019 ICPC 南昌邀请赛 A-Attack(斯坦纳树)相关推荐
- 2019 ICPC南昌邀请赛比赛游记 队伍名:莫比乌斯
南昌队伍是学长临时组的 飞的那天和学长们坐 6.1的飞机 川航还发了棒棒糖 下飞机就落在了座位上了 然后难道大暴雨 我鞋子都湿了 我们和学长去了酒店 酒店是很有情趣味道 滑稽 于是拍下了学长的背影 学 ...
- 2019 ICPC南昌邀请赛网络赛比赛过程及题解
解题过程 中午吃饭比较晚,到机房lfw开始发各队的账号密码,byf开始读D题,shl电脑卡的要死,启动中...然后听到谁说A题过了好多,然后shl让blf读A题,A题blf一下就A了.然后lfw读完M ...
- 2019 icpc南昌邀请赛 G Winner
题目链接:https://nanti.jisuanke.com/t/40259 Ichuan really likes to play games, so he organized a game co ...
- 2019 ICPC全国邀请赛(西安)I. Cracking Password(序列检验,BSGS,细节题)
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 2019 ICPC全国邀请赛(西安)I. Cracking Password Weblink http ...
- 2019 ICPC 南昌网络赛 H. The Nth Item
2019 ICPC 南昌网络赛 H. The Nth Item 题目大意:已知一个数列F(n): F(0)=0,F(1)=1 F(n)=3∗F(n−1)+2∗F(n−2),(n≥2) 给你一个操作 ...
- [学习笔记]斯坦纳树
处理一种这样的问题: 斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种.最小生成树是在给定的点集和边中寻求最短网络使所有点连通.而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络 ...
- bzoj1402 Ticket to Ride 斯坦纳树 + 状压dp
给定\(n\)个点,\(m\)条边的带权无向图 选出一些边,使得\(4\)对点之间可达,询问权值最小为多少 \(n \leqslant 30, m \leqslant 1000\) 首先看数据范围,\ ...
- BZOJ 4006 Luogu P3264 [JLOI2015]管道连接 (斯坦纳树、状压DP)
题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4006 (luogu)https://www.luogu.org/probl ...
- [WC2008]游览计划(斯坦纳树)
[Luogu4294] 题解 : 斯坦纳树 \(dp[i][j]\) 表示以\(i\)号节点为根,当前状态为\(j\)(与\(i\)连通的点为\(1\)) 当根\(i\)不改变时状态转移方程是: \( ...
最新文章
- toString()、String.valueOf、(String)强转,如何抉择,你真的了解吗
- iOS获取最上层控制器
- LCD正向扫描和反向扫描
- Telerik for Winform 2010版下载、安装、初次使用
- 【Linux】scp“免密” 远程copy较多文件
- 2019你还没搭建个人博客吗?进来看看
- scala通过JDBC进行数据库操作
- 百度AI快车道深圳实战班启动,用极致技术打造实用产品
- html绘制圆形和弧形的代码,html5 canvas用来绘制弧形的代码实现
- 如何在Azure中配置SQL Server 2008 R2故障转移群集实例
- 转载--Defunct僵尸进程
- Linux运维基础入门知识
- 计算机应用基础南丁格尔,关于单元基础训练的习题
- 华沙理工大学语言c1,留学波兰华沙理工大学:一个让人轻易就爱上的地方
- kaggle实战:Titanic
- 城市云脑研究之三,人工智能在城市云脑建设中的地位与作用
- HIVE --- Metastore
- 为知笔记(PC端) 康奈尔模板各栏间距调整
- 小米 红米4(标准版)线刷兼救砖_解账户锁_纯净刷机包_教程
- 养老保险不到60岁能领吗