UVa Problem 10160 Servicing Stations (服务站)

本文介绍了一种求解图的最小支配集问题的方法,通过枚举子集结合优化手段,有效地解决了NP完全问题。文章详细阐述了优化策略及其实现细节。
// Servicing Stations (服务站) // PC/UVa IDs: 110804/10160, Popularity: B, Success rate: low Level: 3 // Verdict: Accepted // Submission Date: 2011-08-10 // UVa Run Time: 0.056s // 版权所有(C)2011,邱秋。metaphysis # yeah dot net // // 该题目的实质是求图的最小支配集顶点个数。求图的最小支配集问题是 NP 完全问题,目前无高效的算法。 // 既然要枚举集合的所有子集,那就枚举吧,但是等一等,枚举需要个顺序,先枚举元素数量小的子集,要不 // 然,先枚举元素个数大的子集可能会浪费时间,毕竟从元素个数小的子集枚举到元素个数大的子集可以保证 // 找到最小支配集而不再浪费更多的时间,哎呀,时间是金钱,不是吗?可能什么时候某个强人发现 NP 难问 // 题的 P 时间算法,或者证明 P = NP 那就好了,希望在有生之年能够看到!! // // 为了提高效率,压缩程序运行时间,采用了以下优化方法: // (1)若图可以拆分为多个不相连的子图,则先予拆分,然后对子图求最小支配集的顶点个数相加(之前由 // 于未拆分图,导致了多次 TLE)。 // (2)对于求两个集合的并采用了位操作,事先将某个顶点的邻接表表示为一个整数以便用与操作来代替集合 // 的并。 // (3)枚举时,先考虑度数大的顶点。 // // 枚举方法参考了 [J. Loughry, J.I. van Hemert, L. Schoofs, Efficiently Enumerating // the Subsets of a Set, 2000] #include <iostream> #include <vector> #include <algorithm> #include <set> using namespace std; #define MAX_TOWN 35 typedef long long unsigned LLUINT; bool finish; int minimum; vector < LLUINT > edge; LLUINT target_tag, origin_tag; // 检查顶点集合是否为支配集。 void check(int flag[], int position) { int index = 0; LLUINT new_tag = origin_tag, old_tag = origin_tag; for (int i = 0; i < edge.size(); i++) { if ((index < position) && (flag[index] == i)) { new_tag |= edge[i]; if (new_tag > old_tag) old_tag = new_tag; else return; index++; } } if (new_tag == target_tag) { minimum = index; finish = true; } } // 按 Banker’s Sequence 枚举图的子集。 void generate(int flag[], int position, int positions) { if (finish) return; if (position < positions) { if (position == 0) { for (int i = 0; i < edge.size(); i++) { flag[position] = i; generate(flag, position + 1, positions); } } else { for (int i = flag[position - 1] + 1; i < edge.size(); i++) { flag[position] = i; generate(flag, position + 1, positions); } } } else check(flag, position); } // 枚举图向量的子集以判断是否是一个支配集。 void enumerating_subset() { for (int i = 1; i <= edge.size(); i++) { int * flag = new int[edge.size()]; generate(flag, 0, i); delete [] flag; if(finish) return; } } bool cmp(LLUINT x, LLUINT y) { return x > y; } // 获取图的最小支配集顶点数(MDSN)。 int mdsn(vector < vector < int > > &vertex) { int base = 0; origin_tag = 0; target_tag = 0; vector < bool > dirty(vertex.size()); fill(dirty.begin(), dirty.end(), false); // 清掉度为 0 的点。度为 0 的点和其他点都无通路,则该图的最小支配集必须要包括 // 该顶点。则表示最小支配集顶点个数的变量 base 需增加 1。 for (int i = 0; i < vertex.size(); i++) { if (vertex[i].size() == 0) { base++; origin_tag |= ((LLUINT)1 << i); } if (vertex[i].size() == 1 && dirty[i] == false) { dirty[i] = true; if (dirty[vertex[i][0] - 1] == false) { base++; dirty[vertex[i][0] - 1] = true; } } target_tag |= ((LLUINT)1 << i); } // 清掉度为 1 的点。度为 1 的点表明该顶点 A 只与其他一个顶点 B 相连接,则可 // 将 B 计入最小支配集中。 edge.clear(); for (int i = 0; i < vertex.size(); i++) { if (dirty[i] == true) { origin_tag |= ((LLUINT)1 << i); for (int j = 0; j < vertex[i].size(); j++) origin_tag |= ((LLUINT)1 << (vertex[i][j] - 1)); } if (dirty[i] == false && vertex[i].size() > 0) { LLUINT t = ((LLUINT)1 << i); for (int j = 0; j < vertex[i].size(); j++) t |= ((LLUINT)1 << (vertex[i][j] - 1)); edge.push_back(t); } } // 排序,度数大的点首先考虑。 sort(edge.begin(), edge.end(), cmp); minimum = 0; finish = false; enumerating_subset(); return (base + minimum); } int servicing_stations(vector < vector < int > > &vertex) { // 使用宽度优先搜索分离子图,计算子图的最小支配集顶点个数,相 // 加即为原图的最小支配集顶点个数。 int total = 0; while (vertex.size() > 0) { vector < vector < int > > open; set < int > close; int size = 0; open.push_back(vertex[0]); close.insert(vertex[0][0]); vertex.erase(vertex.begin()); while (open.size() > size) { int origin = size; int current = open.size() - 1; size = open.size(); for (int i = origin; i <= current; i++) { for (int j = 1; j < open[i].size(); j++) { if (close.find(open[i][j]) == close.end()) { close.insert(open[i][j]); for (int m = 0; m < vertex.size(); m++) { if (vertex[m][0] == open[i][j]) { open.push_back(vertex[m]); vertex.erase(vertex.begin() + m); break; } } } } } } // 调整分离出的子图的序号以便后续操作。 vector < vector < int > > tmp; for (int c = 1; c <= MAX_TOWN; c++) if (close.find(c) != close.end()) { for (int i = 0; i < open.size(); i++) if (open[i][0] == c) tmp.push_back(open[i]); } for (int i = 0; i < tmp.size(); i++) { int current = tmp[i][0]; for (int m = 0; m < tmp.size(); m++) for (int n = 1; n < tmp[m].size(); n++) if (tmp[m][n] == current) tmp[m][n] = (i + 1); } for (int i = 0; i < tmp.size(); i++) tmp[i].erase(tmp[i].begin()); // 计算该子图的最小支配集顶点数。 total += mdsn(tmp); } return total; } int main(int ac, char *av[]) { int n; int m; int x, y; vector < vector < int > > vertex; while (cin >> n >> m, n && m) { vertex.clear(); vertex.resize(n); // 数组的第一个数存放顶点的序号。 for (int i = 0; i < n; i++) vertex[i].push_back((i + 1)); // 读入镇之间的通路。 for (int i = 0; i < m; i++) { // 自身连接到自身的通路不添加。 cin >> x >> y; if (x != y) { vertex[x - 1].push_back(y); vertex[y - 1].push_back(x); } } cout << servicing_stations(vertex) << endl; } return 0; }

<think>嗯,用户这次的问题是询问如何卸载Windows的Servicing Stack Update(SSU)。从用户提供的引用资料来看,ta似乎遇到了Windows更新导致的问题(引用3提到更新后鼠标无法使用),并且尝试过常规卸载方法失败。 用户引用的资料中有几个关键点需要注意:引用1和2说明SSU是系统关键组件,微软通常不允许卸载;引用3展示了一种通过修改系统文件强制标记更新为“可卸载”的方法;引用4则是关于组策略编辑器的修复方法,可能卸载无关;引用5提到Win11的兼容性问题。 考虑到SSU的特殊性,直接卸载确实存在风险。用户提到鼠标问题可能是由其他更新引起的(引用3),因此更安全的做法是先排除其他更新。如果用户坚持要处理SSU,引用3提供的修改.mum文件方法虽然存在,但需要强调这属于非常规操作。 用户可能已经尝试过控制面板的常规卸载流程失败,才会寻求强制卸载方案。ta的技术水平应该不错(能理解修改系统文件的操作),但可能低估了SSU的重要性。需要重点警告系统崩溃风险,并提供回退方案建议。 在操作步骤上需要细化:比如修改.mum文件时要同时处理所有相关文件(引用3未明确说明这点),以及卸载后如何验证SSU是否真正被移除。此外引用4提到的组策略编辑器修复方法虽然无关,但可以作为备选建议加入“其他方案”。 </think>卸载Servicing Stack Updates(SSU)是**高风险操作且微软官方不支持**,因为它维护Windows更新的底层框架。强行卸载可能导致系统崩溃、更新失效或安全漏洞。仅在其他更新引发严重问题且确认是SSU导致时尝试以下方法: --- ### ⚠️ 强制卸载步骤(仅限高级用户) **前提条件:** 1. 已确认具体SSU的更新编号(如 `KB5034441`) 2. 创建系统完整备份或还原点 3. 禁用Windows Update服务(防止自动修复) **操作流程:** 1. **打开管理员权限的命令提示符** Win+R输入 `cmd` → 右键选择“以管理员身份运行”。 2. **定位SSU包路径** 输入: ```powershell cd /d C:\Windows\servicing\Packages ``` 3. **查找关联的.mum文件** 输入(替换 `KB5034441` 为你的更新号): ```powershell dir *KB5034441*.mum /s ``` 记录所有匹配的文件路径(通常有多个关联文件)。 4. **卸载SSU组件** 对**每个**找到的.mum文件执行: ```powershell dism /online /Remove-Package /PackagePath:"文件完整路径" /norestart ``` 例如: ```powershell dism /online /Remove-Package /PackagePath:"C:\Windows\servicing\Packages\Package_for_KB5034441~31bf3856ad364e35~amd64~~19041.3636.1.4.mum" /norestart ``` 5. **重启系统** ```powershell shutdown /r /t 0 ``` --- ### 🔧 替代方案(更安全) 若SSU导致设备故障(如引用[3]的鼠标失灵),优先尝试: 1. **卸载近期其他更新** 控制面板 → 程序 → 查看已安装更新 → 按日期排序 → 卸载可疑更新(非SSU)。 2. **系统文件扫描** ```powershell sfc /scannow DISM /Online /Cleanup-Image /RestoreHealth ``` 3. **清理更新缓存** ```powershell net stop wuauserv net stop bits del %windir%\SoftwareDistribution\Download\* /f /s /q net start wuauserv net start bits ``` --- ### ⚠️ 关键警告 1. **系统不稳定风险** SSU卸载后可能导致后续更新失败(错误 `0x80070643`)或系统无法启动[^1][^2]。 2. **安全漏洞** 缺失SSU会使设备暴露于未修复漏洞(微软将其归类为**关键安全更新**)[^2]。 3. **替代方案优先** 强烈建议先尝试卸载其他冲突更新(引用[3])或使用系统还原点回退。 > **提示**:若因SSU导致磁盘空间不足(如WinRE分区问题),微软会发布专用修复工具(如 `KB5034441` 需手动调整分区),而非卸载SSU本身[^1]。 --- ### 🔍 相关问题 1. 如何确认Servicing Stack Update是否安装成功? 2. SSU安装失败报错 `0x80070643` 应如何解决? 3. Windows Update历史记录被清空后如何追踪已安装的SSU? 4. 哪些工具可以安全清理Windows更新缓存而不影响SSU? [^1]: [微软文档:Servicing Stack Updates](https://docs.microsoft.com/en-us/windows/deployment/update/servicing-stack-updates) [^2]: [SSU累积更新的区别说明](https://techcommunity.microsoft.com/t5/windows-it-pro-blog/servicing-stack-updates/ba-p/745219) [^3]: [强制卸载Windows更新的技术方案](https://superuser.com/questions/1437907/uninstalling-uninstallable-windows-updates)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值