题目

这道题的题意理解很重要,直接写原题了。
小林把人体需要的营养分成了\(n\)种,他准备了2套厨师机器人,一套厨师机器人有\(n\)个,每个厨师机器人只会做一道菜,这道菜一斤能提供第\(i\)种营养\(x_i\)微克。想要吃这道菜的时候,只要输入一个数,就能吃到对应数量的这道菜了。为防止摄入过量对身体造成的伤害,每个机器人还有防过量摄入药,只要输入一个数,就能生成一定剂量的药,吃了这些药,就能减少相当于食用对应数目的这道菜提供的营养。
小林之所以准备2套厨师机器人,是因为某一个厨师机器人可能坏掉,要是影响了银河队选手的身体,就不好了。因此,第2套厨师机器人被用来做第1套的备用。小林需要为每一个第1套厨师机器人选一个第2套厨师机器人作备份,使得当这个机器人坏掉时,用备份顶替,整套厨师机器人仍然能搭配出任何营养需求,而且,每个第2套厨师机器人只能当一个第1套厨师机器人的备份。求一种备份方案,使得字典序最小(按所备份的机器人编号从小到大输出)。

分析

首先,题中输入的“数”可以是小数,所以可以任意搭配。那“搭配出任何营养需求”是什么意思呢?也就是说,我可以通过改变每种菜的数量来获取任意组合的营养,即:
\[ \begin{aligned} y_i=\sum _ {j=1}^n x_j *A _{ji} \\\ \end{aligned} \]
我们把他们都写成矩阵的形式:
\[ \begin{aligned} x*A=y \end{aligned} \]
所以判断一个矩阵能否组合出任意营养搭配,就是判断一个矩阵是否能通过消元变成对角阵。这个转化十分关键,其实就是看懂题意。
这是一个一一对应的问题,每个备用机器人只能换一个第一套机器人,然后就想到,这和二分图的匹配模型很相似。以后见到两种物品,一一对应在一个集合中选择给另一个集合匹配(例如gdkoi2017的演员那题)类似的题目,二分图应该是一种可行的解法。
现在的问题是,我们要求出每一个\(B\_j\)有那些\(A\_i\)可以替换。这里用到了一个很显然的结论。
\(A\)可以通过消元变成对角阵。若存在行向量\(\lambda\)使得\(\lambda\*A=b\),那么如果\(\lambda \_ i\ne 0\),那么把行\(A\_i\)替换为\(b\)后,新的矩阵也能消元得出对角阵。这是因为,一行不能被替换,当且仅当替换后这一行会被消成全0,即替换后这一行可以用别的行线性表示(每行乘以一个不为0的系数,行相加),于是就无法消元得出满对角阵。所以我们只能选择\(\lambda\)不为0的行替换。
由于\(\lambda\)有\(n\)个,不如我们把它们全部一起求出来。矩阵\(C\)表示所有的\(\lambda\),即\(C\_j\)表示\(B\_j\)的替换方案:
\[ C*A=B \\ C*A*{A^{-1}}=B*{A^{-1}} \\ C=B*A^{-1} \\ \]
这样我们就求出了所有的\(\lambda\),即求出了二分图的邻接矩阵,\(C_{ij}\ne 0\)表示左边的\(j\)和右边的\(i\)有连边。这样就可以求二分图匹配了。现在的问题在于,如何求一个矩阵的逆矩阵。
例如原矩阵为:
\[ \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \]
我们给出增广炬阵,并把左边消元成单位阵:
\[ \left[ \begin{array}{cc|cc} 1 & 2 & 1 & 0 \\ 3 & 4 & 0 & 1 \\ \end{array} \right] \\ \left[ \begin{array}{cc|cc} 1 & 2 & 1 & 0 \\ 0 & -2 & -3 & 1 \\ \end{array} \right] \\ \left[ \begin{array}{cc|cc} 1 & 0 & 1 & 1 \\ 0 & -2 & -3 & 1 \\ \end{array} \right] \\ \left[ \begin{array}{cc|cc} 1 & 0 & -2 & 1 \\ 0 & 1 & \frac{3}{2} & -\frac{1}{2} \\ \end{array} \right] \\ \]
这时,右边得到的矩阵就是原矩阵的逆矩阵。所以矩阵求逆非常简单,只要对左边进行消元,变成对角阵即可。如果一个矩阵不可逆,那么左边无法形成对角阵。
最后一个问题,如何解决字典序最小。通过匈牙利算法的过程可以看出,我们只能保证得到的最后一个匹配的字典序最小,前面的无法保证。因此,我们可以通过一种调整来在匹配数量不变的情况下从前往后保证最小字典序。从小到大,对于左边的一个点,先把原来的匹配边记录下来并断开,再从小到大扫右边的点,跟之前一样找增广路。由于之前断开了匹配边,匹配数量减一;找到增广路后,匹配数量加一,故答案不变。只要找到了增广路,我们就把当前的左边点和右边点删掉(标记),把它们锁定,这样以后不会再更改到它们。这样既可求出最大匹配的最小字典序。其实最小字典序就是一个贪心的东西。

代码

用double直接消元比他们的大数取模快几倍,不过大数取模也是很好的方法。通过给定一个永远不会超过的大质数,可以方便地求乘法逆元,解决除法问题,不过每次多一个log。

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
using namespace std;
int read() {int x=0,f=1;char c=getchar();for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;for (;isdigit(c);c=getchar()) x=x*10+c-'0';return x*f;
}
const int maxn=305;
const double eps=1e-9;
int b[maxn][maxn],match[maxn];
double a[maxn][maxn],c[maxn][maxn];
double h[maxn][maxn<<1];
int n;
bool alr[maxn];
int gcd(int x,int y) {if (x<y) swap(x,y);return y?gcd(y,x%y):x;
}
bool elm() {for (int i=1;i<n;++i) {if (!h[i][i]) {for (int j=i+1;j<=n;++j) if (fabs(h[j][i])>eps) {for (int k=1;k<=n+n;++k) swap(h[i][k],h[j][k]);break;}}for (int j=i+1;j<=n;++j) if (h[j][i]) {double tmp=h[j][i]/h[i][i];for (int k=1;k<=n+n;++k) h[j][k]-=h[i][k]*tmp,h[j][k]=(fabs(h[j][k])<eps?0:h[j][k]);}       }for (int i=n;i>1;--i) {if (!h[i][i]) {for (int j=i-1;j>0;--j) if (fabs(h[j][i])<eps) {for (int k=1;k<=n+n;++k) swap(h[i][k],h[j][k]);break;}   }   for (int j=i-1;j>0;--j) if (h[j][i]) {double tmp=h[j][i]/h[i][i];for (int k=1;k<=n+n;++k) h[j][k]-=h[i][k]*tmp,h[j][k]=(fabs(h[j][k])<eps?0:h[j][k]);}       }for (int i=1;i<=n;++i) if (fabs(h[i][i])<eps) return false;return true;
}
bool abx[maxn],aby[maxn];
bool dfs(int x) {for (int i=1;i<=n;++i) if (fabs(c[i][x])>eps && !alr[i] && !aby[i])  {alr[i]=true;if (!match[i] || dfs(match[i])) {match[i]=x;return true;}}return false;
}
void change(int x) {int k;for (int i=1;i<=n;++i) if (match[i]==x) {k=i;break;}match[k]=0;for (int i=1;i<=n;++i) if (fabs(c[i][x])>eps && !alr[i] && !aby[i]) {alr[i]=true;if (!match[i] || (!abx[match[i]] && dfs(match[i]))) {match[i]=x;abx[x]=true;aby[i]=true;return;}}abx[x]=aby[k]=true;match[k]=x;
}
int Ans[maxn];
int main() {#ifndef ONLINE_JUDGEfreopen("test.in","r",stdin);freopen("my.out","w",stdout);#endifn=read();for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) h[i][j]=read();for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) b[i][j]=read();for (int i=1;i<=n;++i) h[i][i+n]=1;bool flag=elm();if (!flag) {puts("NIE");return 0;}for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) a[i][j]=(double)h[i][j+n]/h[i][i];for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) for (int k=1;k<=n;++k) c[i][j]+=b[i][k]*a[k][j];int ans=0;for (int i=1;i<=n;++i) memset(alr,0,sizeof alr),ans+=dfs(i);if (ans!=n) {puts("NIE");return 0;} else puts("TAK");for (int i=1;i<=n;++i) {memset(alr,0,sizeof alr);change(i);}for (int i=1;i<=n;++i) Ans[match[i]]=i;for (int i=1;i<=n;++i) printf("%d\n",Ans[i]);
}

转载于:https://www.cnblogs.com/owenyu/p/6724724.html

bzoj3168-钙铁锌硒维生素相关推荐

  1. bzoj3168 钙铁锌硒维生素 (矩阵求逆+二分图最小字典序匹配)

    设第一套为A,第二套为B 先对于每个B[i]判断他能否替代A[j],即B[i]与其他的A线性无关 设$B[i]=\sum\limits_{k}{c[k]*A[k]}$,那么只要看c[j]是否等于零即可 ...

  2. 【HEOI2013】bzoj3168 钙铁锌硒维生素

    Description 银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加 宇宙比赛的饮食.众所周知,前往宇宙的某个星球,通常要花费好长好长的时间,人体情况在这之间会发生变化,因此,需 ...

  3. [ 高斯消元 二分图最大匹配 ] [ HEOI2013 ] BZOJ3168 钙铁锌硒维生素

    题解 #include<bits/stdc++.h> using namespace std; const int N=310; const int P=1000000007; int k ...

  4. 【BZOJ3168】[Heoi2013]钙铁锌硒维生素 高斯消元求矩阵的逆+匈牙利算法

    [BZOJ3168][Heoi2013]钙铁锌硒维生素 Description 银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加宇宙比赛的饮食.众所周知,前往宇宙的某个星球,通常要花 ...

  5. 洛谷 P4100 [HEOI2013]钙铁锌硒维生素 解题报告

    P4100 [HEOI2013]钙铁锌硒维生素 题目描述 银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加 宇宙比赛的饮食. 众所周知,前往宇宙的某个星球,通常要花费好长好长的时间, ...

  6. BZOJ 3168: [Heoi2013]钙铁锌硒维生素 [线性基 Hungary 矩阵求逆]

    3168: [Heoi2013]钙铁锌硒维生素 题意:给一个线性无关组A,再给一个B,要为A中每个向量在B中选一个可以代替的向量,替换后仍然线性无关.判断可行和求字典序最小的解 PoPoQQQ orz ...

  7. Bzoj3168 [Heoi2013]钙铁锌硒维生素

    Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 466  Solved: 151 Description 银河队选手名单出来了!小林,作为特聘的营养师 ...

  8. BZOJ3168: [Heoi2013]钙铁锌硒维生素

    传送门 题意 给定一个满秩的矩阵 AAA ,另一个矩阵 BBB 对于 AAA 的每个行向量 AiA_iAi​ 找到一个匹配 BBB 的行向量 BpiB_{p_i}Bpi​​ 使得 AiA_iAi​ 替 ...

  9. BZOJ3168. 【HEOI2013】钙铁锌硒维生素

    题目大意 给定两个n∗nn*n的矩阵A,BA,B,求一个字典序最小的排列aa满足将任意一个AiA_i换成BaiB_{a_i}后,矩阵AA仍然满秩. Data Constraint n≤300n\leq ...

  10. BZOJ3168. [HEOI2013]钙铁锌硒维生素(线性代数+二分图匹配)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=3168 题解 首先,我们需要求出对于任意的 \(i, j(1 \leq i, j \leq ...

最新文章

  1. Hibernate的配置文件 Hibernate.cfg.xml与xxx.hbm.xml
  2. 项目进度管理:定义活动
  3. mysql主从同步-问题梳理
  4. Linux系统语言教程,Linux操作系统基础及语言基础教程-麦可网张凌华
  5. 初步使用计算机学设计,幼儿园计算机教学设计参考
  6. UVA11428 Cubes【数学+二分】
  7. php 量 高并发 nosql,nosql - 高并发下Apache+mongodb的php驱动不稳定
  8. Docker 外部访问容器Pp、数据管理volume、网络network 介绍
  9. jsp文件创建后自动设置其pageEncoding的属性值为UTF-8
  10. LBP算法 人脸识别特征提取
  11. 做中学之五笔输入法实践教程
  12. 应用数学软件测试题,数学与应用数学专业试题.doc
  13. vue使用Swiper页面中有滚动条,为什么鼠标滚轮不起作用呢?
  14. Redis高频面试题(欢迎来学习讨论)
  15. 9091端口及8998端口
  16. easywechat6获取微信用户信息(基于公众号)
  17. MySQL完全卸载干净(图解)
  18. shp文件的可视化与编辑
  19. 关于RC延时电路的 时间常数 和 到达某电压的延时时间 计算
  20. DC/DC电源介绍及应用要点

热门文章

  1. python多任务,线程详解
  2. 【亲测好用】磁盘管理器:Disk Xray Mac版
  3. macOS上的符号链接Symlink是什么,以及该怎么使用
  4. 你需要的iPhoneX适配
  5. 【MAC M1芯片】PS已解决在M1苹果电脑上出现“液化”和WEB等黑屏问题
  6. 7.腾讯微博Android客户端开发——自动获取验证码(1)
  7. oracle数据库什么情况下创建索引比较好
  8. 1031 查验身份证 (15 分) python
  9. 一文读懂生成对抗网络(GANs)【下载PDF | 长文】
  10. 201943 ovf 文件的导出和导入