HNOI2016模拟4.10 线性代数与逻辑 简化条件后的简单DP
2016-04-12 17:43
363 查看
题目大意
对0101矩阵AA,BB定义运算C=A⊗BC = A \otimes B,满足Cij=Aij⊗BijC_{ij} = A_{ij} \otimes B_{ij},其中a⊗b=b∨¬aa \otimes b = b\vee \neg a,对应的c++代码为b || !ab ~ ||~ !a。现在给你一个AA矩阵,要求你求出一个BB矩阵,要求A⊗BA \otimes B为全11的矩阵并且存在一个序列CC,使得 ∀Bij=Ci\forall B_{ij} = C_i ^CjC_j。有TT组测试数据,每组数据给出一个N∗NN*N的矩阵AA,要求输出BB中最多有多少个11。T≤100T \leq 100,N≤1000N \leq 1000,∑N2≤2000000\sum N^2 \leq 2000000
解题思路
分析一下AA,BB矩阵的性质,由于A⊗BA \otimes B得到的是一个全11矩阵,根据⊗\otimes的运算原则,如果AijA_{ij}等于11,那么可以得出BijB_{ij}必定为11,因为如果BijB_{ij}不为1就不满足得到矩阵全为11的性质。那么顺带可得出Ci=CjCi = Cj ^ 11。而当AijA_{ij}等于00时,那BijB_{ij}取什么值都是合法的,也就是对CiC_i和CjC_j没有限制。那么我们再来分析一下答案什么叫最大化矩阵BB中11的个数,其实就是在符合条件的情况下使CC序列中00的个数乘11的个数最大。分析清楚题目的本质后,那么思路就清晰了。由于矩阵是对称的,我们只需考虑对角线以上的情况,最后把答案乘2就可以了。
对于不能相等的AijA_{ij}等于11的情况我们就可以从ii向jj连一条边保证CiC_i不能等于CjC_j。那么构出来就要求是一个二分图,如果不是肯定无解。那么CC序列中的点就会分成很多联通快,而对于一个联通快,我们可以是二分图左边的点为11,右边为00或者反过来。这样对于每一个联通块就有两种方案。由于最后答案是所有联通块的00的个数乘11的个数,为了保证答案最优,我们再用一个DpDp,FijF_{ij}表示做到第ii个联通块,00的个数为jj时11最多有多少个,由于每个联通块只有两种情况,所以简单转移一下就可以得出答案了。
代码
//YxuanwKeith #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 1e3 + 5, MAXM = MAXN * MAXN * 4; int N, Num, Fa[MAXN], Col[MAXN], Cnt[MAXN][2], F[MAXN][MAXN], A[MAXN][MAXN]; int tot, Next[MAXM], Last[MAXN], Go[MAXM]; bool Flag; int Link(int u, int v) { Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v; } int Get(int Now) { return (Fa[Now] == Now) ? Now : Fa[Now] = Get(Fa[Now]); } void Dfs(int Now, int Pre) { Cnt[Num][Col[Now]] ++; for (int p = Last[Now]; p; p = Next[p]) { int v = Go[p]; if (v == Pre) continue; if (Col[v] != -1 && Col[v] == Col[Now]) {Flag = 1; return;} if (Col[v] != -1) continue; Col[v] = Col[Now] ^ 1; Dfs(v, Now); if (Flag) return; } } void GetBlock() { memset(Last, 0, sizeof Last); for (int i = 1; i <= N; i ++) Fa[i] = i; for (int i = 1; i <= N - 1; i ++) for (int j = i + 1; j <= N; j ++) { if (!A[i][j]) continue; Link(i, j), Link(j, i); int F1 = Get(i), F2 = Get(j); if (F1 != F2) Fa[F1] = F2; } } void Work() { scanf("%d", &N); Flag = 0; for (int i = 1; i <= N; i ++) for (int j = 1; j <= N; j ++) scanf("%d", &A[i][j]); for (int i = 1; i <= N; i ++) if (A[i][i] == 1) Flag = 1; tot = Num = 0; GetBlock(); memset(Col, 255, sizeof Col), memset(Cnt, 0, sizeof Cnt), memset(F, 255, sizeof F); for (int i = 1; i <= N; i ++) if (Get(i) == i) { Num ++, Col[i] = 0; Dfs(i, 0); if (Flag) break; } if (Flag) {printf("-1\n"); return;} F[0][0] = 0; int Ans = 0; for (int i = 1; i <= Num; i ++) { for (int j = 0; j <= N; j ++) { int Num0 = Cnt[i][0], Num1 = Cnt[i][1]; if (j >= Num0 && F[i - 1][j - Num0] != -1) F[i][j] = max(F[i][j], F[i - 1][j - Num0] + Num1); if (j >= Num1 && F[i - 1][j - Num1] != -1) F[i][j] = max(F[i][j], F[i - 1][j - Num1] + Num0); if (i == Num) Ans = max(Ans, F[i][j] * j); } } printf("%d\n", Ans * 2 - tot); } int main() { int Test; scanf("%d", &Test); for (; Test; Test --) Work(); }
相关文章推荐
- JVM(4)--java堆和栈、内存分配策略
- HM编码器代码阅读(4)——一些概念
- You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE)
- 深入理解C++指针
- 关于java写进mysql中文乱码问题
- #find ./ -type f | xargs sed -i "s///g"
- Linux就这个范儿 第19章 团结就是力量 LSB是Linux标准化基地(Linux Standards Base)的简称
- [Laravel] Laravel的基本数据库操作部分
- 初学python(对比java语言不同) 第八篇
- Android Wear SDK 中文 ---- 表盘设计 [Designing Watch Face]
- 点击输入框只掉出数字键盘
- HM编码器代码阅读(3)——一些比较容易混淆的类和结构
- Activity,Window,View之间的关系
- 技术开篇
- QT 加载c语言编译的动态库
- Hadoop 2.4.0+zookeeper3.4.6+hbase0.98.3分布式集群搭建
- cas单点登录搭建
- POJ 1321(DFS)
- RabbitMQ入门(四) —— topic交换器
- Android动画——Tween动画之Translate