【BZOJ3168】[Heoi2013]鈣鐵鋅硒維生素 高斯消元求矩陣的逆+匈牙利算法
阿新 • • 發佈:2017-06-20
def strong bzoj light sof turn 防止 宇宙 !=
第一行包含一個正整數n。
接下來n行,每行n個整數,表示第1套廚師機器人做的菜每一斤提供的每種營養。
再接下來n行,每行n個整數,表示第2套廚師機器人做的菜每一斤提供的每種營養。
1≤n≤300,所有出現的整數均非負,且不超過10,000。
1 0 0
0 1 0
0 0 1
2 3 0
0 7 8
0 0 9
1
2
3
【BZOJ3168】[Heoi2013]鈣鐵鋅硒維生素
Description
銀河隊選手名單出來了!小林,作為特聘的營養師,將負責銀河隊選手參加宇宙比賽的飲食。眾所周知,前往宇宙的某個星球,通常要花費好長好長的時間,人體情況在這之間會發生變化,因此,需要根據每天的情況搭配夥食,來保證營養。小林把人體需要的營養分成了n種,這些營養包括但不限於鐵,鈣。他準備了2套廚師機器人,一套廚師機器人有n個,每個廚師機器人只會做一道菜,這道菜一斤能提供第i種營養xi微克。想要吃這道菜的時候,只要輸入一個數,就能吃到對應數量的這道菜了。為防止攝入過量對身體造成的傷害,每個機器人還有防過量攝入藥,只要輸入一個數,就能生成一定劑量的藥,吃了這些藥,就能減少相當於食用對應數目的這道菜提供的營養。小林之所以準備2套廚師機器人,正是因為旅途漫漫,難以預計,也許某一個廚師機器人在途中壞掉,要是影響了銀河隊選手的身體,就不好了。因此,第2套廚師機器人被用來做第1套的備用。小林需要為每一個第1套廚師機器人選一個第2套廚師機器人作備份,使得當這個機器人壞掉時,用備份頂替,整套廚師機器人仍然能搭配出任何營養需求,而且,每個第2套廚師機器人只能當一個第1套廚師機器人的備份。Input
Output
第一行是一個字符串,如果無法完成任務,輸出“NIE”,否則輸出“TAK” 並跟著n行,第i行表示第i個第1套機器人的備份是哪一個第2套機器人。 為了避免麻煩,如果有多種可能的答案,請給出字典序最小的那一組。Sample Input
31 0 0
0 1 0
0 0 1
0 7 8
0 0 9
Sample Output
TAK1
2
3
題解:要不是有大爺的題解我還真就不一定能看懂題意。。。
題目大意:給定一個n∗n的滿秩矩陣A和一個n∗n的矩陣B,求一個字典序最小的1...n的排列a滿足將任意一個Ai換成Bai後矩陣A仍然滿秩。
是不是清晰多了?求這樣的排列實際上是將A乘上一個矩陣C使得CA=B,C=AB-1。
求出來C後,我們改變思路,將用邊i->j表示B中j號機器人能替換A中i號機器人,然後就得到了一個二分圖,而CT正好是二分圖的鄰接矩陣,我們想求二分圖的字典序最小的完美匹配。
一開始以為大爺做麻煩了,直接從後往前做一遍匈牙利算法就行,知道我看了Discuss。。。好吧還是要做兩邊,第一遍正常跑,第二遍貪心的選編號小的,並且要求後面的點不能影響前面的點。
這裏依舊是做一下大爺博客的註釋:
1.為什麽是CA=B而不是AC=B???(我一開始也死活搞不懂)
因為每個機器人對應一個行向量,而我們想讓A中的行向量對應B中另外一個行向量,這顯然直接矩乘是不行的,但如果我們將A,B分別轉置,就可以做到A中一個列向量對應B中另外一個列向量,然後就是ATC=BT(這裏的C指的就是二分圖的鄰接矩陣),等價於CTA=B
2.不影響前面的交錯環是什麽鬼?(或者你認為我說的也不是那麽清楚?)
直接說方法吧:在第二遍DFS的時候記錄一下當前是從那個點開始去尋找增廣路的,也就意味著編號比這個點小的點我們都不能影響。所以我們看一下B中的機器人所對應的A中的機器人的編號,如果編號<起始點,就不能更改;如果編號=起始點,而我們已經向讓起始點和其它點匹配了,這個點就已經被閑置出來了,就可以更改;如果編號>起始點,那麽我們繼續尋找增廣路就行了。
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #define mod 999911657 using namespace std; const double eps=1e-6; typedef long long ll; int n,ans; ll A[310][610],B[310][310],C[310][310]; int vis[310],from[310],to[310]; ll pm(ll x,ll y) { ll z=1; while(y) { if(y&1) z=z*x%mod; x=x*x%mod,y>>=1; } return z; } int rd() { int ret=0; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) gc=getchar(); while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret; } int dfs1(int x) { for(int i=1;i<=n;i++) { if(C[i][x]&&!vis[i]) { vis[i]=1; if(!from[i]||dfs1(from[i])) { to[x]=i,from[i]=x; return 1; } } } return 0; } int dfs2(int x,int y) { for(int i=1;i<=n;i++) { if(C[i][x]&&!vis[i]) { vis[i]=1; if(from[i]==y||(from[i]>y&&dfs2(from[i],y))) { from[i]=x,to[x]=i; return 1; } } } return 0; } int main() { n=rd(); int i,j,k; for(i=1;i<=n;i++) for(j=1;j<=n;j++) A[i][j]=rd(),A[i][j+n]=(i==j); for(i=1;i<=n;i++) for(j=1;j<=n;j++) B[i][j]=rd(); ll t; for(i=1;i<=n;i++) { for(j=i;j<=n;j++) if(A[j][i]) { for(k=1;k<=2*n;k++) swap(A[i][k],A[j][k]); break; } t=pm(A[i][i],mod-2); for(k=i;k<=2*n;k++) A[i][k]=A[i][k]*t%mod; for(j=1;j<=n;j++) if(i!=j) { t=A[j][i]; for(k=1;k<=2*n;k++) A[j][k]=(A[j][k]-t*A[i][k]%mod+mod)%mod; } } for(i=1;i<=n;i++) for(j=1;j<=n;j++) for(k=1;k<=n;k++) C[i][j]=(C[i][j]+B[i][k]*A[k][j+n])%mod; for(i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); ans+=dfs1(i); } if(ans<n) { printf("NIE"); return 0; } printf("TAK\n"); for(i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); dfs2(i,i); } for(i=1;i<=n;i++) printf("%d\n",to[i]); return 0; }
【BZOJ3168】[Heoi2013]鈣鐵鋅硒維生素 高斯消元求矩陣的逆+匈牙利算法