紫书刷题进行中,题解系列【GitHub|CSDN】
例题6-21 UVA506 System Dependencies(67行AC代码)
题目大意
把每个命令要做的事理清,代码很简单
本题的问题背景极有实际意义,例如
- Linux下各种包管理器
- 开发应用程序时配置管理,如springboot框架等
给定组件的依赖关系,根据命令输出相应信息,四种命令如下:
DEPEND item1 item2 [item3 ...]
:item1 依赖于item2 (and item3 …)INSTALL item1
:安装 item1 和它的依赖(若有多个依赖则按照输入依赖的顺序处理)REMOVE item1
:移除item1,和它的依赖(当且仅当它的依赖为隐式安装且此时不再被其它组件依赖;若有多个依赖则按照输入依赖的顺序处理)LIST
:按安装顺序输出已安装的组件
其中install item成功的项目称作显示安装
算法设计
为了提高效率,将字符串缓存与数组中,并用map存储相应编号,后续所有操作均存储id即可
map<string, int> idMp; // 名字->id
vector<string> name; // 组件名字缓存
为了便于处理依赖关系,定义before和after
,其中before[i]
表示i依赖的前置组件,after[i]
表示依赖于i的后置组件;定义status
区分安装状态;installed
仅仅是为了List命令服务
map<int,vector<int> > before, after; // 依赖的项目,被项目依赖
vector<int> installed; // 已安装列表
int status[maxn]={0}; // 0:未安装;1:已显示安装;2:隐式安装
至于安装和删除都可以递归进行(输出顺序不同,对应为后序和先序),注意只有显示删除命令才可以删除显式安装
注意点
- 样例输出中倒数第二个删除命令的
Removing HTML和 Removing TCPIP
顺序反了 - 注意安装时已存在,删除时不存在的情况
AC代码(C++11,字符串处理,命令处理)
#include<bits/stdc++.h>
using namespace std;
const int maxn=10010; // 最大点数
map<string, int> idMp; // 名字->id
vector<string> name; // 组件名字缓存
map<int,vector<int> > before, after; // 依赖的项目,被项目依赖
vector<int> installed; // 已安装列表
int status[maxn]={0}; // 0:未安装;1:已显示安装;2:隐式安装
int getId(string s) { // 手动分配id
if (idMp.find(s) == idMp.end()) { // 不存在,手动分配
idMp[s] = name.size();
name.push_back(s);
}
return idMp[s]; // 已存在,直接返回
}
void List() { // 按安装顺序打印已安装列表
for (auto s : installed) printf(" %s\n", name[s].c_str());
}
void Install(int i, bool isTop) { // 递归安装
if (status[i] != 0 && isTop) printf(" %s is already installed.\n", name[i].c_str()); // 已安装
if (status[i] == 0) { // 未安装
for (int j : before[i]) Install(j, false); // 递归安装前置依赖
printf(" Installing %s\n", name[i].c_str());
status[i] = (isTop ? 1 : 2); // 顶部为显示依赖,其余为隐式依赖
installed.push_back(i); // 存于安装列表
}
}
bool isNeed(int i) { // 判断i是否被依赖
for (int j : after[i]) if (status[j] != 0) return true; // 遍历后置节点
return false;
}
void Remove(int r, bool isTop) { // 递归移除
if (status[r] == 0 && isTop) printf(" %s is not installed.\n", name[r].c_str()); // 未安装
if (status[r] != 0 && isTop && isNeed(r)) printf(" %s is still needed.\n", name[r].c_str()); // 存在依赖
if (status[r] != 0 && !isNeed(r)) { // 不被需要
printf(" Removing %s\n", name[r].c_str());
status[r] = 0; // 状态置0,未安装
installed.erase(find(installed.begin(), installed.end(), r)); // 删除安装列表中相应信息
for (int j : before[r]) if (status[j] == 2) Remove(j, false); // 仅删除隐式依赖
}
}
int main() {
string line, cmd, sa, sb;
while (getline(cin, line)) {
cout <<line <<endl; // 输出原命令
if (line[0] == 'E') { // 初始化数据结构
idMp.clear(); name.clear(); before.clear(); after.clear(); installed.clear();
memset(status, 0, sizeof(status));
continue; // 进入下一组数据输入
}
stringstream input(line); // 流分割
input >>cmd; // 命令
if (cmd[0] == 'L') List(); // 显示已安装列表
else {
input >>sa;
if (cmd[0] == 'D') { // 增加依赖
while (input >>sb) {
before[getId(sa)].push_back(getId(sb)); // sa依赖于sb
after[getId(sb)].push_back(getId(sa)); // sb被sa依赖
}
}
else if (cmd[0] == 'I') Install(getId(sa), true); // 安装
else if (cmd[0] == 'R') Remove(getId(sa), true); // 删除
}
}
return 0;
}