CTSC1999补丁VS错误题解
2015-07-15 16:39
232 查看
题目描述 Description
错误就是人们所说的Bug。用户在使用软件时总是希望其错误越少越好,最好是没有错误的。但是推出一个没有错误的软件几乎不可能,所以很多软件公司都在疯狂地发放补丁(有时这种补丁甚至是收费的)。T公司就是其中之一。
上个月,T公司推出了一个新的字处理软件,随后发放了一批补丁。最近T公司发现其发放的补丁有致命的问题,那就是一个补丁在排除某些错误的同时,往往会加入另一些错误.
此字处理软件中只可能出现n个特定的错误,这n个错误是由软件本身决定的。T公司目前共发放了m个补丁,对于每一个补丁, 都有特定的适用环境,某个补丁只有在当前软件中包含某些错误而同时又不包含另一些错误时才可以使用,如果它被使用,它将修复某些错误而同时加入某些错误。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。
更准确地说明:
设此字处理软件中可能出现的n个错误为集合B={b1,b2,…,bn}中的元素,T公司目前共发放了m个补丁:p1,p2,…,pm。对于每一个补丁pi, 都有特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以用,为了说明清楚,设错误集合:Bi+、 Bi-, 当软件包含了Bi+中的所有错误, 而没有包含Bi-中的任何错误时,补丁Pi才可以被使用,否则不能使用,显然 Bi+、Bi-交集为空。补丁pi将修复某些错误而同时加入某些错误,设错误集合Fi-、Fi+,使用过补丁pi之后,Fi-中的任何错误都不会在软件中出现,而软件将包含Fi+中的所有错误, 同样Fi-、Fi+交集为空。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。
现在T公司的问题很简单,其字处理软件的初始版本不幸地包含了集合B中的全部n个错误, 有没有可能通过使用这些补丁(任意顺序地使用,一个补丁可使用多次), 使此字处理软件成为一个没有错误的软件。如果可能,希望找到总耗时最少的方案。
输入描述 Input Description
输入文件第一行有两个正整数n和m, n表示错误总数,m表示补丁总数。接下来m行给出了m个补丁的信息。每行包括一个正整数(表示此补丁程序pi的运行耗时)和两个长度为n的字符串,中间用一个空格符隔开。
第一个字符串,如果第k个字符为’+’,则表示bk属于Bi+, 若为‘-’,则表示bk属于Bi-, 若为‘0’,则bk 既不属于Bi+也不属于Bi-,即软件中是否包含bk不影响补丁pi是否可用。
第二个字符串,如果第k个字符为’+’,则表示bk属于Fi+, 若为‘-’,则表示bk属于Fi-, 若为‘0’,则bk 既不属于Fi+也不属于Fi-,即软件中是否包含bk不会因使用补丁pi而改变。
输出描述 Output Description
输出一个整数,如果问题有解,输出总耗时,否则输出0。
样例输入 Sample Input
3 3
1 000 00-
1 00- 0-+
2 0– -++
样例输出 Sample Output
8
数据范围及提示 Data Size & Hint
1≤n≤20,1≤m≤1001\le n\le20, 1\le m\le100
不过据说n最大只有15。按15做的可以过。
题解
用二进制存错误的所有错误状态,共2n2^n种。下面的程序中,用reqt[i](require true)表示第i个补丁需要有哪些错误(二进制表示,下同),reqf[i](require false)表示第i个补丁需要没有哪些错误,用rest[i](result in true)表示装了第i个补丁后出现了哪些新的错误,用resf[i](result in false)表示装了第i个补丁后消除了哪些错误,timi表示第i个补丁运行的时间。空间复杂度约为O(2n)O(2^n),完全可以承受。
初始状态为2n−12^n-1(二进制),末状态为0。
枚举所有状态和补丁,如果某状态可以装某补丁,即从该状态添一条指向装补丁之后的状态的有向边(别添反了),边权为该补丁运行的时间。这之后再从初始状态开始跑堆优化dijkstra最短路即可。时间复杂度O((m+n)2n)O((m+n)2^n),n=20可能就过不了了。不过既然搜索都可以过,那这个算法就没有不过的理由了。
Code
错误就是人们所说的Bug。用户在使用软件时总是希望其错误越少越好,最好是没有错误的。但是推出一个没有错误的软件几乎不可能,所以很多软件公司都在疯狂地发放补丁(有时这种补丁甚至是收费的)。T公司就是其中之一。
上个月,T公司推出了一个新的字处理软件,随后发放了一批补丁。最近T公司发现其发放的补丁有致命的问题,那就是一个补丁在排除某些错误的同时,往往会加入另一些错误.
此字处理软件中只可能出现n个特定的错误,这n个错误是由软件本身决定的。T公司目前共发放了m个补丁,对于每一个补丁, 都有特定的适用环境,某个补丁只有在当前软件中包含某些错误而同时又不包含另一些错误时才可以使用,如果它被使用,它将修复某些错误而同时加入某些错误。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。
更准确地说明:
设此字处理软件中可能出现的n个错误为集合B={b1,b2,…,bn}中的元素,T公司目前共发放了m个补丁:p1,p2,…,pm。对于每一个补丁pi, 都有特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以用,为了说明清楚,设错误集合:Bi+、 Bi-, 当软件包含了Bi+中的所有错误, 而没有包含Bi-中的任何错误时,补丁Pi才可以被使用,否则不能使用,显然 Bi+、Bi-交集为空。补丁pi将修复某些错误而同时加入某些错误,设错误集合Fi-、Fi+,使用过补丁pi之后,Fi-中的任何错误都不会在软件中出现,而软件将包含Fi+中的所有错误, 同样Fi-、Fi+交集为空。另外,使用每个补丁都要耗一定的时间(即补丁程序的运行时间)。
现在T公司的问题很简单,其字处理软件的初始版本不幸地包含了集合B中的全部n个错误, 有没有可能通过使用这些补丁(任意顺序地使用,一个补丁可使用多次), 使此字处理软件成为一个没有错误的软件。如果可能,希望找到总耗时最少的方案。
输入描述 Input Description
输入文件第一行有两个正整数n和m, n表示错误总数,m表示补丁总数。接下来m行给出了m个补丁的信息。每行包括一个正整数(表示此补丁程序pi的运行耗时)和两个长度为n的字符串,中间用一个空格符隔开。
第一个字符串,如果第k个字符为’+’,则表示bk属于Bi+, 若为‘-’,则表示bk属于Bi-, 若为‘0’,则bk 既不属于Bi+也不属于Bi-,即软件中是否包含bk不影响补丁pi是否可用。
第二个字符串,如果第k个字符为’+’,则表示bk属于Fi+, 若为‘-’,则表示bk属于Fi-, 若为‘0’,则bk 既不属于Fi+也不属于Fi-,即软件中是否包含bk不会因使用补丁pi而改变。
输出描述 Output Description
输出一个整数,如果问题有解,输出总耗时,否则输出0。
样例输入 Sample Input
3 3
1 000 00-
1 00- 0-+
2 0– -++
样例输出 Sample Output
8
数据范围及提示 Data Size & Hint
1≤n≤20,1≤m≤1001\le n\le20, 1\le m\le100
不过据说n最大只有15。按15做的可以过。
题解
用二进制存错误的所有错误状态,共2n2^n种。下面的程序中,用reqt[i](require true)表示第i个补丁需要有哪些错误(二进制表示,下同),reqf[i](require false)表示第i个补丁需要没有哪些错误,用rest[i](result in true)表示装了第i个补丁后出现了哪些新的错误,用resf[i](result in false)表示装了第i个补丁后消除了哪些错误,timi表示第i个补丁运行的时间。空间复杂度约为O(2n)O(2^n),完全可以承受。
初始状态为2n−12^n-1(二进制),末状态为0。
枚举所有状态和补丁,如果某状态可以装某补丁,即从该状态添一条指向装补丁之后的状态的有向边(别添反了),边权为该补丁运行的时间。这之后再从初始状态开始跑堆优化dijkstra最短路即可。时间复杂度O((m+n)2n)O((m+n)2^n),n=20可能就过不了了。不过既然搜索都可以过,那这个算法就没有不过的理由了。
Code
#include <cstdio> #include <algorithm> #include <cstring> #include <queue> using namespace std; const int maxn = (1 << 17), oo = 1000000000, nil = 0; int N, M, tim[105], reqt[105], reqf[105], rest[105], resf[105]; int e, pnt[maxn], nxt[maxn << 2], u[maxn << 2], v[maxn << 2], w[maxn << 2]; int d[maxn]; bool vis[maxn]; struct node { int n, dis; node(int n = 0, int dis = 0) :n(n), dis(dis) {} bool operator < (const node& b) const { return dis > b.dis; } }; void addedge(int a, int b, int c) { u[++e] = a; v[e] = b; w[e] = c; nxt[e] = pnt[a]; pnt[a] = e; } void init() { char ch[25]; scanf("%d%d", &N, &M); for(int i = 1; i <= M; ++i) { scanf("%d", &tim[i]); scanf("%s", &ch); for(int j = 0; j < N; ++j) { if(ch[j] == '+') { reqt[i] |= (1 << j); } if(ch[j] == '-') { reqf[i] |= (1 << j); } } scanf("%s", &ch); for(int j = 0; j < N; ++j) { if(ch[j] == '+') { rest[i] |= (1 << j); } if(ch[j] == '-') { resf[i] |= (1 << j); } } } for(int i = (1 << N) - 1; i >= 0; --i) { for(int j = 1; j <= M; ++j) { if(((i & reqt[j]) == reqt[j]) && ((i & reqf[j]) == 0)) { addedge(i, (i & (~resf[j]) | rest[j]), tim[j]); } } } } void work() { int S = (1 << N) - 1, T = 0; memset(d, 0x3f, sizeof(d)); memset(vis, 0, sizeof(vis)); priority_queue <node> Q; d[S] = 0; Q.push(node(S, 0)); while(!Q.empty()) { node tmp = Q.top(); Q.pop(); vis[tmp.n] = true; for(int j = pnt[tmp.n]; j != nil; j = nxt[j]) { if((!vis[v[j]]) && d[v[j]] > tmp.dis + w[j]) { d[v[j]] = tmp.dis + w[j]; Q.push(node(v[j], d[v[j]])); } } } if(d[T] > oo) { puts("0"); } else { printf("%d\n", d[T]); } } int main() { init(); work(); return 0; }
相关文章推荐
- Android获取meta-data
- ecshop广告调用方法
- SWFUpload学习记录
- linux中的设备节点
- 基于Aspose.Words 生成Word文件
- C++ Json处理
- eclipse Package Explorer的设置探索
- Afinal的FinalActivity的注解学习笔记
- C# 结构体访问器错误提示
- 2.2-1 文章模块开发 【入口脚本及模板的创建】
- Linux系统性能分析
- TestFlight——完美的iOS app测试方案
- 如何学习hybris?
- 栈、堆
- 删除表中前N条以外的数据
- poj 3321 dfs + 树状数组
- YT05-动态归划求解课后题目-1001—FatMouse's Speed-(6.21日-烟台大学ACM预备队解题报告)
- secureCRT使用VIM时对语法高亮
- Hive不同文件的读取与序列化
- 前端性能优化和规范