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
31 0 0
0 1 0
0 0 1
2 3 0
0 7 8
0 0 9
Sample Output
TAK1
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]鈣鐵鋅硒維生素