BZOJ 3168 [Heoi2013]钙铁锌硒维生素

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3168 [Heoi2013]钙铁锌硒维生素相关的知识,希望对你有一定的参考价值。

Description

银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加宇宙比赛的饮食。众所周知,前往宇宙的某个星球,通常要花费好长好长的时间,人体情况在这之间会发生变化,因此,需要根据每天的情况搭配伙食,来保证营养。小林把人体需要的营养分成了n种,这些营养包括但不限于铁,钙。他准备了2套厨师机器人,一套厨师机器人有n个,每个厨师机器人只会做一道菜,这道菜一斤能提供第i种营养xi微克。想要吃这道菜的时候,只要输入一个数,就能吃到对应数量的这道菜了。为防止摄入过量对身体造成的伤害,每个机器人还有防过量摄入药,只要输入一个数,就能生成一定剂量的药,吃了这些药,就能减少相当于食用对应数目的这道菜提供的营养。小林之所以准备2套厨师机器人,正是因为旅途漫漫,难以预计,也许某一个厨师机器人在途中坏掉,要是影响了银河队选手的身体,就不好了。因此,第2套厨师机器人被用来做第1套的备用。小林需要为每一个第1套厨师机器人选一个第2套厨师机器人作备份,使得当这个机器人坏掉时,用备份顶替,整套厨师机器人仍然能搭配出任何营养需求,而且,每个第2套厨师机器人只能当一个第1套厨师机器人的备份。

Input

第一行包含一个正整数n。
接下来n行,每行n个整数,表示第1套厨师机器人做的菜每一斤提供的每种营养。
再接下来n行,每行n个整数,表示第2套厨师机器人做的菜每一斤提供的每种营养。
1≤n≤300,所有出现的整数均非负,且不超过10,000。

Output

第一行是一个字符串,如果无法完成任务,输出“NIE”,否则输出“TAK”
并跟着n行,第i行表示第i个第1套机器人的备份是哪一个第2套机器人。
为了避免麻烦,如果有多种可能的答案,请给出字典序最小的那一组。
 

Sample Input

3
1 0 0
0 1 0
0 0 1
2 3 0
0 7 8
0 0 9
 

Sample Output

TAK
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]钙铁锌硒维生素的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3168 [Heoi2013]钙铁锌硒维生素

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

儿童钙铁锌什么牌子好?这篇文章给你答案!

BZOJ 3166: [Heoi2013]Alo

BZOJ 3167: [Heoi2013]Sao

[BZOJ3167][Heoi2013]Sao