当前位置: 首页 > news >正文

UVa11211 Digital Logic

UVa11211 Digital Logic

  • 题目链接
  • 题意
    • 输入格式
    • 输出格式
  • 分析
  • 测试数据生成
  • AC 代码

题目链接

  UVa11211 Digital Logic

题意

  给你一些 2 输入 1 输出的逻辑门,你的任务是设计一个 4 输入 4 输出的数字电路。每一个逻辑门由 3 个 0-1 整数 Y00, Y01 和 Y11 描述。代表输入分别为 0,1,2 个 1 时的输出。注意所有的逻辑门都是对称的,所以当有一个输入被设置为 1 时,不管是哪一个输入门,输出的结果都相同。任何一个逻辑门的输入端都是另外一个逻辑门的输出端或者 4 个源输入端之一。由于当任何一个输入被悬挂时输出结果将不可预料,所以务必保证一个逻辑门的输出将不会再次变为其输入,不管是直接的还是间接的(换句话说,逻辑电路将不包含环路),如下图所示。
Digital Logic
  为了让设计更加简单,你需要使用尽可能少的逻辑门。题目保证所有输入数据都可以用最多 6 个逻辑门实现。

输入格式

  输入数据最多有 30 组,每组数据第一行是一个整数 n(n<6) 代表逻辑门的种类。接下来 n 行每行包括 4 个整数 mi, Y00, Y01, Y11,分别表示该类型逻辑门的数目,以及输入端分别有 0,1,2 个 1 时的输出。逻辑门的总数量最多只有 10(所有种类逻辑门的数量之和最多为 10)。接下来一行包括 16 个整数 Y0000, Y0001,Y0010, …, Y1111 代表所有可能输入组合的输出情况。Ypqrs 的二进制形式是代表当输入端分别是 p,q,r,s 时输出端 a,b,c,d 端的取值。最后一组数据以一行单个 0 结尾。

输出格式

  对于每组数据输出数据的组数和一个整数 p,代表可以达到要求的最少逻辑门数量。接下来 p 行 s, k, a 和 b,这里 s 是端口的序号(4 个输入端口的编号是 1∼41\sim414,逻辑门的编号是 5∼p+45\sim p+45p+4),k 是该逻辑门的种类(种类的编号按照输入顺序依次编号为 1∼n1\sim n1n),a 和 b 分别是该逻辑门的输入端口编号。注意必须满足 a<s 和 b<s。最后一行包含 4 个整数,为 4 个输出端口的编号(应该在 1∼p+41\sim p+41p+4 之间)。每组数据之后输出一个空行。

分析

  本题限时 15s,本人前 67 次提交基本是TLE,第 68 次提交AC,运行时间 1s 左右。真的有种诗词里面“行到水穷处,坐看云起时”、“山重水复疑无路,柳暗花明又一村”的感觉。

  基本框架是 BFS 并用集合对状态判重。主要难点在于状态设计以及状态拓展时的各种剪枝。先来看一下状态,必然要存每一个使用了的逻辑门种类、两个输入端口(输出端口可以暗含在状态中:从 0 开始编号的第 i 个实际使用的逻辑门其输出端口是 i+5),其他服务于剪枝的必要信息也可能会存入状态中。

  怎么知道一个状态是结束状态呢?即此状态的所有输出中选出 4 个进行排列组合恰好能形成输入指定的 Ypqrs 的形式。选 4 个还要排列组合检验能否形成输入指定的 Ypqrs 的形式,那么代价在 A144=24024A^4_{14} = 24024A144=24024,每次为了判断状态是否是结束状态就要花 20000 次枚举的话显然超时。可以想到对各个端口的输出和输入指定的最终 16 个输出进行转化,由于最终输出一定来自 4 个端口 a,b,c,d(可以有重复使用的端口),可以将最终 16 个输出按照端口转存成 4 个 16 位的二进制数(每一位依次代表输入是 0∼150\sim15015 时此端口输出的是 0 还是 1),这个转换顺便分析出了重复端口(两个相同的16 位的二进制即是重复的)。那么状态里面也需要用一个 16 位的二进制数存每个逻辑门的输出,再判断其是否是结束状态就很简单了:已经知道转移前状态得到了几个需要的输出,只需再看转移后新加的逻辑门是否增加了需要的输出,这需要将已经得到了几个输出也存入状态中。

  几个剪枝:

  • 当前已经使用了 x 个逻辑门,由于“题目保证所有输入数据都可以用最多 6 个逻辑门实现”,那么此后做状态拓展时最多能新得到 6-x 个需要的输出,也就是说如果当前状态已经得到 c 个(不同的)需要的输出,实际需要的输出去重后数量是 cc ,那么 c+6−x<ccc+6-x < ccc+6x<cc 时可以剪枝。
  • 如果尝试新添加一个逻辑门,其输出在旧的端口中已经存在,那么不需要添加它,可以剪枝。
  • 任何状态,其未作为其他端口输入又不是最终需要的输出的逻辑门数量不超过 3 (虽然实际可以超过3,但是加这一个限制能减小枚举量并且不会错过最优解),并且当已经使用了 5 个逻辑门时不超过 2, 已经使用了 6 个逻辑门时则应该为 0。
  • 任何状态,其未作为其他端口输入又不是最终需要的输出的那些逻辑门种类编号如果大于最新加入的逻辑门种类编号,则可以忽略此状态,这个剪枝的作用也是减小枚举量并且不会错过最优解。
  • 任何状态,如果其未作为其他端口输入又不是最终需要的输出的逻辑门数量为 3,那么进行状态拓展一定是从这些逻辑门中选两个或者选一个且从其他类型的旧逻辑门中再选一个作为新添加的逻辑门的输入端口。

测试数据生成

  刘汝佳老师出了这个好题,却没在 uDebug 上提供比较耗时的测试数据的例子,这让我每次新做了剪枝优化就认为自己能 AC 🙃🤣😂。于是写了一份生成测试数据的 python 脚本:

# -*- coding: utf-8 -*-from random import randint, choice, sampleif __name__ == '__main__':with open('in.txt', 'w') as f:for _ in range(2000):n = randint(1, 5)s, g, cc, t = randint(n, 10), [], [], 0for i in range(n):a, b, c, ok = randint(0, 1), randint(0, 1), randint(0, 1), Falsewhile not ok:a, b, c = randint(0, 1), randint(0, 1), randint(0, 1)ok = not ((a==1 and b==1 and c==1) or (a==0 and b==0 and c==0))for j in range(i):m = g[j][0]ai, bi, ci = g[j][1]if a == ai and b == bi and c == ci:ok = Falsebreakm = randint(1, s-t-(n-1-i))t += mcc.append(m)g.append((m, [a, b, c]))k, ina, inb = [i for i in range(t)], [i for i in range(t)], [i for i in range(t)]for i in range(t):s = []for j in range(n):if cc[j] > 0:s.append(j)v = choice(s)cc[v] -= 1k[i], ina[i], inb[i] = v, choice([x for x in range(i+4)]), choice([x for x in range(i+4)])f.write(f'{n}\n')for i in range(n):m = g[i][0]a, b, c = g[i][1]cc.append(c)f.write(f'{m} {a} {b} {c}\n')out, vis = [], [False] * (t+4)for i in range(t):vis[ina[i]] = vis[inb[i]] = Truefor i in range(t):if not vis[i+4]:out.append(i+4)if randint(0, 1) == 1 and len(out) == 1:out = [t+3, t+3, t+3, t+3]elif len(out) < 4:for i in sample([x for x in range(t+4)], 4-len(out)):out.append(i)p = randint(0, 15)a, b, c, d = p>>3, (p>>2)&1, (p>>1)&1, p&1v = [x for x in range(t+4)]for x in range(16):v[0], v[1], v[2], v[3] = x>>3, (x>>2)&1, (x>>1)&1, x&1for i in range(t):v[i+4] = g[k[i]][1][v[ina[i]]+v[inb[i]]]f.write(f'{v[out[a]]<<3 | v[out[b]]<<2 | v[out[c]]<<1 | v[out[d]]} ')f.write('\n')f.write('0\n')

  贴一份有利于各位做优化剪枝分析的测试数据:
  输入

1
6 0 1 1
0 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15
2
2 0 0 1
2 1 0 1
15 0 0 0 0 0 0 15 15 0 0 0 0 0 0 15
5
3 1 0 1
3 1 0 0
1 0 1 0
2 1 1 0
1 0 1 1
15 15 11 11 11 11 4 4 11 11 15 15 11 11 4 4
3
3 1 0 1
2 0 1 1
2 1 0 0
9 9 0 6 9 9 6 0 6 0 9 15 0 6 15 9
4
4 1 0 0
1 1 1 0
1 0 0 1
2 0 1 1
1 1 1 1 0 1 1 1 1 1 1 1 14 1 15 1
1
10 1 1 0
12 15 12 12 12 15 12 12 3 15 0 15 3 15 0 15
5
3 0 0 0
1 0 1 0
2 1 1 0
1 0 0 1
2 1 0 1
5 5 0 5 15 15 10 15 15 15 10 5 15 15 10 5
2
1 1 0 1
7 1 1 0
15 10 15 0 15 10 15 0 15 10 15 0 5 15 5 15
1
8 1 0 0
6 9 6 9 6 9 6 0 0 9 0 0 0 9 0 0
5
6 1 0 1
1 0 1 0
1 1 0 0
1 0 0 1
1 1 1 0
15 0 10 0 15 0 0 15 0 0 10 15 0 0 0 0
5
3 1 0 1
2 0 1 1
1 0 0 1
1 1 1 0
1 1 0 0
9 0 9 0 9 9 9 0 0 9 6 15 6 15 0 9
5
1 0 1 1
3 0 1 0
1 1 0 0
3 1 1 0
1 0 0 1
14 0 14 14 14 1 14 14 1 0 1 14 1 1 1 14
0

  输出,注意本题的最优解实例不唯一。

Case 1: 3
5 1 4 3
6 1 5 2
7 1 6 1
7 7 7 7Case 2: 3
5 2 4 2
6 2 4 3
7 1 6 5
7 7 7 7Case 3: 3
5 4 3 2
6 2 2 1
7 3 6 3
5 7 5 5Case 4: 5
5 1 3 1
6 1 4 3
7 1 6 2
8 3 3 1
9 3 8 7
5 9 9 5Case 5: 5
5 1 4 2
6 4 4 3
7 4 6 5
8 2 2 1
9 1 8 4
9 9 9 7Case 6: 6
5 1 4 1
6 1 5 1
7 1 5 3
8 1 7 1
9 1 7 4
10 1 9 8
6 6 10 10Case 7: 6
5 4 4 3
6 2 5 2
7 3 6 1
8 3 7 1
9 5 8 2
10 5 5 3
9 10 9 10Case 8: 6
5 2 2 1
6 2 3 3
7 2 6 4
8 2 7 5
9 1 8 4
10 2 5 4
9 10 9 10Case 9: 6
5 1 4 1
6 1 5 3
7 1 2 1
8 1 7 6
9 1 8 4
10 1 9 8
10 5 5 10Case 10: 6
5 2 2 1
6 3 4 1
7 5 5 3
8 1 7 6
9 1 4 3
10 4 9 8
8 10 8 10Case 11: 6
5 1 3 2
6 5 5 1
7 1 6 5
8 1 4 1
9 3 6 2
10 2 9 8
10 7 7 10Case 12: 6
5 1 4 1
6 4 4 3
7 2 6 5
8 4 6 2
9 5 8 4
10 2 9 5
7 7 7 10

AC 代码

#include <iostream>
#include <algorithm>
#include <set>
using namespace std;#define N 16
int y[N][3], v1[N], v2[N], r[4], rr[4], a[4], out[4], n, t, m, cc, ans, kase = 0;
struct node {short k[N], a[N], b[N], c[N], m; int v[N]; bool f[N];} q[1000000];struct cmp {bool operator() (int i, int j) const {for (int k=0; k<ans; ++k) v1[k] = q[i].v[k+5], v2[k] = q[j].v[k+5];sort(v1, v1+ans); sort(v2, v2+ans);for (int k=0; k<ans; ++k) if (v1[k] != v2[k]) return v1[k] < v2[k];return false;}
};
set<int, cmp> ss;bool comp(const node &a, const node &b) {int c1 = 0, c2 = 0;for (int i=1; i<16; i<<=1) {if (a.m & i) ++ c1;if (b.m & i) ++ c2;}return c1 > c2;
}bool check(int p) {m = q[t].v[p] = 0;for (int i=0; i<N; ++i) q[t].v[p] |= y[q[t].k[p]][(q[t].v[q[t].a[p]]>>i&1) + (q[t].v[q[t].b[p]]>>i&1)] << i;for (int i=1; i<p; ++i) if (q[t].v[i] == q[t].v[p]) return false;if (q[t].v[p] != r[0] && q[t].v[p] != r[1] && q[t].v[p] != r[2] && q[t].v[p] != r[3]) q[t].f[p] = true;for (int i=5; i<=p; ++i) if (q[t].f[i] && (q[t].k[i] > q[t].k[p] || ans==6 || (ans==5 && m>1) || ++m > 3)) return false;return true;
}bool ext(const node &e, int p, int i, int j, int k) {q[t] = e; q[t].k[p] = i; q[t].a[p] = j; q[t].b[p] = k; --q[t].c[i]; q[t].f[j] = q[t].f[k] = q[t].f[p] = false;if (check(p) && !ss.count(t)) {for (int i=0; i<cc; ++i) if (q[t].v[p] == rr[i]) ++q[t].m;if (q[t].m == cc) return true;if (q[t].m >= cc-6+ans) ss.insert(t++);}return false;
}bool ext(const node &e) {int p = ans+4, m = 0;for (int i=5; i<p; ++i) if (e.f[i]) a[m++] = i;for (int i=1; i<=n; ++i) if (e.c[i] > 0) {if (ans == 6 && m) {if (m == 1) {for (int j=1; j<p; ++j) if ((j<5 || !e.f[j]) && ext(e, p, i, max(a[0], j), min(a[0], j))) return true;} else if (ext(e, p, i, a[1], a[0])) return true;} else if (m == 3) {if (ext(e, p, i, a[2], a[1]) || ext(e, p, i, a[2], a[0]) || ext(e, p, i, a[1], a[0])) return true;while (m--) for (int j=1; j<p; ++j) if ((j<5 || !e.f[j]) && ext(e, p, i, max(a[m], j), min(a[m], j))) return true;} else for (int j=1; j<p; ++j) for (int k=1; k<=j; ++k) if (ext(e, p, i, j, k)) return true;}return false;
}void print() {for (int i=0, k=ans+5; i<4; ++i) for (int j=1; j<k; ++j) if (q[t].v[j] == r[i]) {out[i] = j; break;}cout << "Case " << ++kase << ": " << ans << endl;for (int i=5, j=ans+5; i<j; ++i) cout << i << ' ' << q[t].k[i] << ' ' << q[t].a[i] << ' ' << q[t].b[i] << endl;cout << out[0] << ' ' << out[1] << ' ' << out[2] << ' ' << out[3] << endl << endl;
}void solve() {for (int i=1; i<=n; ++i) cin >> q[0].c[i] >> y[i][0] >> y[i][1] >> y[i][2];for (int i=q[t=0].m=0; i<4; ++i) r[i] = 0, q[0].v[i+1] = 0;for (int i=ans=0; i<N; ++i) {int v; cin >> v; r[0] |= v>>3<<i; r[1] |= (v>>2&1)<<i; r[2] |= (v>>1&1)<<i; r[3] |= (v&1)<<i;q[0].v[1] |= i>>3<<i; q[0].v[2] |= (i>>2&1)<<i; q[0].v[3] |= (i>>1&1)<<i; q[0].v[4] |= (i&1)<<i;}rr[0] = r[0]; rr[1] = r[1]; rr[2] = r[2]; rr[3] = r[3]; sort(rr, rr+4); cc = unique(rr, rr+4) - rr;for (int i=0; i<cc; ++i) for (int j=1; j<=4; ++j) if (rr[i] == q[0].v[j]) {++q[0].m; break;}if (q[0].m == cc) return print();ss.clear();for (int h=0, c=ans=t=1; h<c;) {if (ext(q[h])) return print();if (++h == c) {if (h+1 < t) sort(q+h+1, q+t, comp);c = t; ++ans; ss.clear();}}
}int main() {while (cin >> n && n) solve();return 0;
}
http://www.dtcms.com/a/428202.html

相关文章:

  • 在门户网站做产品seowordpress 上传中文文件名
  • 营销网站建站开发整站seo策略实施
  • Day04_总线驱动
  • 成都市成华区建设局官方网站wordpress啦去
  • 901-008_高级系统架构设计师-考试范围-系统质量属性与架构评估
  • 重庆做网站seo优化选哪家好php数据库的网站模板
  • RMBG2.0 vs. BiRefNet_HR:从「人像抠图」到「万物分割」
  • 怎么做外卖网站网站怎么被搜到首页
  • 无锡做公司网站西固网页设计
  • 【K8s】升级节点
  • Spring Boot自定义全局异常处理:从痛点到优雅实现
  • 网站正在建设中的素材动图网站设计制作程序
  • 企业网站的搭建流程河南论坛网站建设
  • wap网站建设策划方案做艺术字的网站
  • 电脑实用工具,资源下载
  • 曲阳有没有做网站里2345网站登录
  • PostgreSQL 向量操作符的计算和使用方式
  • 动态代理在提升网络安全中的作用及应用
  • 宁夏微信服务网站百度网盘资源搜索
  • 手机做网站用什么软件微信下载官方正版
  • Redis缓存异常
  • 建设网站iss局机关门户网站建设情况汇报
  • 做网站需要哪些东西163免费注册入口
  • 【Rust GUI开发入门】编写一个本地音乐播放器(9. 制作设置面板)
  • 概率统计中的数学语言与术语2
  • 美国2025年网络演习全景与趋势洞察
  • 公司做网站有什么用编程和做网站那个号
  • 做公司网站都需要什么免费广告设计网站
  • IO-link 协议高频工业 RFID 读写器
  • NeurIPS 2025 | 北大等提出C²Prompt:解耦类内与类间知识,攻克FCL遗忘难题!