@codeforces - 632F@ Magic Matrix
@description@
给定一个 n*n 的矩阵 A。
我们称 A 是 magic 的,当且仅当:
(1)A 是对称的。
(2)A 的主对角线 \(a_{ii} = 0\)。
(3)对于每一组 (i, j, k) 满足 \(a_{ij} \le \max\{a_{ik}, a_{jk}\}\)。
判断给出的 A 是不是 magic 的。
Input
第一行一个整数 n (1 ≤ n≤ 2500) 。
接下来 n 行包含 n 个整数,描述矩阵 A。
注意 A 不一定对称或者主对角线全为 0。
Output
如果是 magic 的,输出 "MAGIC";否则输出 "NOT MAGIC"。
Examples
Input
3
0 1 2
1 0 2
2 2 0
Output
MAGIC
Input
2
0 1
2 3
Output
NOT MAGIC
@solution@
首先第 1, 2 个条件直接判。主要是考虑第 3 个条件。
解决这道题有一步很关键:将矩阵 A 看成一个图 G 的邻接矩阵。
虽然隔壁的 zjx 大佬告诉我这个已经是套路了,可是菜如我并不熟悉这种套路。
考虑假如不满足第 3 个条件,就有 w(i, j) > w(i, k) 且 w(i, j) > w(k, j)。
即 (i, j) 与两个边权比它严格小的边形成了三元环。
可以发现三元环这个条件限制太严格,不太好判断。
假如 (i, j) 与若干个边权比它严格小的边形成了环,设形成的环为 i -> p1 -> p2 -> ... -> j -> i。这个情况是否也不合法呢?
考虑边 (i, p2),如果 w(i, p2) >= w(i, j) 则 i -> p1 -> p2 -> i 本身就形成了不合法的三元环。
如果 w(i, p2) >= w(i, j),则可以把环缩减为 i -> p2 -> ... p -> j -> i。递归验证必然可以验证到三元环。
所以只要形成环就一定不合法。
那么算法就很明晰了。将边权排序,从小到大加入边。
如果加入到一条边形成了环,则不合法。
同种边权先查询后同时加入。
时间复杂度 \(O(n^2\log(n^2))\)。
@accepted code@
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 2500;
const int MAXM = MAXN*MAXN/2;
struct edge{
int x, y, k;
friend bool operator < (const edge &a, const edge &b) {
return a.k < b.k;
}
}e[MAXM + 5];
int fa[MAXN + 5];
int find(int x) {
return fa[x] = (fa[x] == x ? x : find(fa[x]));
}
void unite(int x, int y) {
int fx = find(x), fy = find(y);
if( fx != fy ) fa[fx] = fy;
}
int A[MAXN + 5][MAXN + 5];
int main() {
int n, cnt = 0; scanf("%d", &n);
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++)
for(int j=1;j<=n;j++) {
if( A[i][j] != A[j][i] ) {
puts("NOT MAGIC");
return 0;
}
if( i == j && A[i][j] ) {
puts("NOT MAGIC");
return 0;
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<i;j++)
cnt++, e[cnt].k = A[i][j], e[cnt].x = i, e[cnt].y = j;
fa[i] = i;
}
sort(e + 1, e + cnt + 1);
e[cnt + 1].k = -1;
for(int i=1;i<=cnt;i++) {
int j = i;
while( e[j+1].k == e[i].k ) j++;
for(int k=i;k<=j;k++)
if( find(e[k].x) == find(e[k].y) ) {
puts("NOT MAGIC");
return 0;
}
for(int k=i;k<=j;k++)
unite(e[k].x, e[k].y);
i = j;
}
puts("MAGIC");
}
@details@
其实这道题还可以用最小生成树的思路解。
这样的话,用 prim 求最小生成树就是 O(n^2) 的,少个 log。
但是时限 5s 就没必要了。
原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11789145.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【数据相关】目标检测中的数据标注及格式转换代码
- 科学与艺术的融合:遗传算法绘制蒙娜丽莎
- “工业听诊”中多声源事件检测与定位
- 工业党福利:使用PaddleX高效实现指针型表计读取系列文章(2)
- 【三维点云系列】PCL点云库之数据文件与IO操作
- Jvm故障处理工具
- 递增子序列
- redis学习(二)
- You-Get 使用方法
- 接口测试 Mock 实战 | 结合 jq 完成批量化的手工 Mock
- 在Angular应用的child Component里同时使用@Input和@Output
- Angular应用里的@Input和@Output注解使用方法介绍
- K8S Ingress使用|常见问题列表
- 部署Tomcat及负载均衡
- Jenkins常用插件Publish Over SSH