BZOJ 3168 [Heoi2013]钙铁锌硒维生素
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3168 [Heoi2013]钙铁锌硒维生素相关的知识,希望对你有一定的参考价值。
Description
银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加宇宙比赛的饮食。众所周知,前往宇宙的某个星球,通常要花费好长好长的时间,人体情况在这之间会发生变化,因此,需要根据每天的情况搭配伙食,来保证营养。小林把人体需要的营养分成了n种,这些营养包括但不限于铁,钙。他准备了2套厨师机器人,一套厨师机器人有n个,每个厨师机器人只会做一道菜,这道菜一斤能提供第i种营养xi微克。想要吃这道菜的时候,只要输入一个数,就能吃到对应数量的这道菜了。为防止摄入过量对身体造成的伤害,每个机器人还有防过量摄入药,只要输入一个数,就能生成一定剂量的药,吃了这些药,就能减少相当于食用对应数目的这道菜提供的营养。小林之所以准备2套厨师机器人,正是因为旅途漫漫,难以预计,也许某一个厨师机器人在途中坏掉,要是影响了银河队选手的身体,就不好了。因此,第2套厨师机器人被用来做第1套的备用。小林需要为每一个第1套厨师机器人选一个第2套厨师机器人作备份,使得当这个机器人坏掉时,用备份顶替,整套厨师机器人仍然能搭配出任何营养需求,而且,每个第2套厨师机器人只能当一个第1套厨师机器人的备份。
Input
Output
Sample Input
1 0 0
0 1 0
0 0 1
2 3 0
0 7 8
0 0 9
Sample Output
1
2
3
这道题是在找二分图相关的题时候找到的,看了以后发现建图才是关键。题意一开始并没有理解清楚,一定要仔细读题,一定要仔细读题,一定要仔细读题。
题目一开始给了两个矩阵A,B,其实就是看B的某一行能代替A的哪些行,通过这个建图,之后直接跑最大匹配验证一下就可以了。这样我们把矩阵A看作是n个向量,每一行都代表一个n维向量,以他们建立n维坐标系,因为题目保证一开始能搭配出任何营养需求,那就是说这个n维空间每个地方都可以被这些向量所表示出来,而且这样的话矩阵A就显然是一个满秩矩阵了,并且B替换A的一行时,还要保证换完以后A还是一个满秩矩阵。
之后我们设出一个系数矩阵C,使得C * A = B,我们现在想C的实际意义,AB中每个机器人对应着一个行向量,所以行向量对应 就需要是CA=B而不是AC=B,如果说C的某一个位置是0 (假设是第i行 第j列),那么也就意味着并不用A中的第j行来表示B中的第i行,这两个行向量是无关的。或者可以理解成,在这个n维空间里,这两个向量是垂直的,也就是我们不能用矩阵B的第i行来代替矩阵A的第j行。
这样只需要求出C就可以了,只要C(i,j) != 0那么B的第i行就能够代替A的第j行 。由于题目保证A是满秩的,所以可以直接求A的逆矩阵A-1,可得C=B A-1依此建图即可,不过题目要求是A的代替方案字典序最小,所以实际建图的邻接矩阵是C的转置矩阵。
跑二分图匹配由于要求字典序最小,所以跑完美匹配,如果可行,再去由小到大贪心的去换边,也就是再跑一次匈牙利算法。(有人说实际上数据并没有保证A是满秩的,所以高斯消元的时候判断一下。)
下面是代码:(比较丑)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 double a[310][310],b[310][310],c[310][310],ni[310][310]; 8 int n,mat[310],ans[310]; 9 bool vis[310],nxt[310][310]; 10 bool ling(double x){ 11 if(fabs(x)<1e-8) return true; 12 return false; 13 } 14 bool gauss(){ 15 int i,j,k; 16 double t; 17 for(i=1;i<=n;i++){ 18 for(j=i;j<=n;j++) if(ling(a[j][i])==false) break; 19 if(i!=j) for(k=1;k<=n;k++) swap(a[i][k],a[j][k]),swap(ni[i][k],ni[j][k]); 20 if(ling(a[i][i])==true) return false; 21 t=1/a[i][i]; 22 for(k=1;k<=n;k++) a[i][k]*=t,ni[i][k]*=t; 23 for(j=1;j<=n;j++){ 24 if(i==j) continue; 25 t=a[j][i]; 26 for(k=1;k<=n;k++){ 27 a[j][k]-=a[i][k]*t; 28 ni[j][k]-=ni[i][k]*t; 29 } 30 } 31 } 32 return true; 33 } 34 void jucheng(){ 35 int i,j,k; 36 for(i=1;i<=n;i++){ 37 for(j=1;j<=n;j++){ 38 for(k=1;k<=n;k++){ 39 c[i][j]+=b[i][k]*ni[k][j]; 40 } 41 } 42 } 43 for(i=1;i<=n;i++){ 44 for(j=1;j<=n;j++){ 45 if(ling(c[i][j])==false) nxt[j][i]=true; 46 else nxt[j][i]=false; 47 } 48 } 49 } 50 bool dfs1(int pos){ 51 for(int i=1;i<=n;i++){ 52 if(nxt[pos][i]==false||vis[i]==false) continue; 53 vis[i]=false; 54 if(mat[i]==0||dfs1(mat[i])==true){ 55 mat[i]=pos; 56 ans[pos]=i; 57 return true; 58 } 59 } 60 return false; 61 } 62 bool dfs2(int pos,int frm){ 63 for(int i=1;i<=n;i++){ 64 if(nxt[pos][i]==false||vis[i]==false) continue; 65 vis[i]=false; 66 if(mat[i]==frm||(mat[i]>frm&&dfs2(mat[i],frm)==true)){ 67 mat[i]=pos; 68 ans[pos]=i; 69 return true; 70 } 71 } 72 return false; 73 } 74 int main() 75 { 76 int i,j; 77 scanf("%d",&n); 78 for(i=1;i<=n;i++){ 79 for(j=1;j<=n;j++){ 80 scanf("%lf",&a[i][j]); 81 } 82 } 83 for(i=1;i<=n;i++){ 84 for(j=1;j<=n;j++){ 85 scanf("%lf",&b[i][j]); 86 } 87 } 88 memset(c,0,sizeof(c)); 89 memset(ni,0,sizeof(ni)); 90 memset(mat,0,sizeof(mat)); 91 for(i=1;i<=n;i++) ni[i][i]=1; 92 if(gauss()==false){ 93 printf("NIE\n"); 94 return 0; 95 } 96 jucheng(); 97 for(i=1;i<=n;i++){ 98 memset(vis,true,sizeof(vis)); 99 if(dfs1(i)==false){ 100 printf("NIE\n"); 101 return 0; 102 } 103 } 104 for(i=1;i<=n;i++){ 105 memset(vis,true,sizeof(vis)); 106 dfs2(i,i); 107 } 108 printf("TAK\n"); 109 for(i=1;i<=n;i++) printf("%d\n",ans[i]); 110 }
以上是关于BZOJ 3168 [Heoi2013]钙铁锌硒维生素的主要内容,如果未能解决你的问题,请参考以下文章