 /**//*
題意:有N個工件要在M個機器上加工,有一個N*M的矩陣描述其加工時間。
同一時間內每個機器只能加工一個工件,問加工完所有工件后,使得平均加工時間最小。

將工件作為二分圖中X部的點,總共N個。
將每個機器拆成N個點作為二分圖中Y部的點,總共N*M個。
第J個機器的第P個點代表,使用機器 J進行倒數第P次加工。
假設我們按順序在J機器上工件I1,I2,I3..IK個工件,則總共需要花費I1*K+I2*(K- 1)+I3*(K-3)+ +IK。
所以我們對于X中每個點I,Y中每個點(J,P),連接一條A[I,J]*P權值的邊。
接下來進行二分圖最佳匹配即可。
*/
#include<cstdio>
#include<cstring>

const int MAXN=55;
const int INF=1000000000;

int n,m;
int g[MAXN][MAXN*MAXN];
int data[MAXN][MAXN];
int lx[MAXN],ly[MAXN*MAXN];//可行頂標
int cx[MAXN],cy[MAXN*MAXN];//匹配邊集
bool sx[MAXN],sy[MAXN*MAXN];

 bool path(int u) {//擴展二分圖 判斷是否存在由行元素u出發的可增廣路徑
sx[u]=1;
 for(int v=1;v<=m;v++) {
 if(g[u][v]==lx[u]+ly[v]&&!sy[v]) {
sy[v]=1;
 if(cy[v]==0||path(cy[v])) {
cx[u]=v;cy[v]=u;
return true;
}
}
}
return false;
}
 int KM() {
memset(ly,0,sizeof(ly));
memset(cx,0,sizeof(cx));
memset(cy,0,sizeof(cy));
 for(int i=1;i<=n;i++) {
lx[i]=-INF;
for(int j=1;j<=m;j++)
if(lx[i]<g[i][j])lx[i]=g[i][j];
}
 for(int u=1;u<=n;u++) {
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
 while(!path(u)) {
int Min=INF;
 for(int i=1;i<=n;i++) {
if(!sx[i])continue;
for(int j=1;j<=m;j++)
if(!sy[j]&&lx[i]+ly[j]-g[i][j]<Min)
Min=lx[i]+ly[j]-g[i][j];
}
//調整頂標,二分圖撤去該元素
for(int i=1;i<=n;i++)
 if(sx[i]) {lx[i]-=Min;sx[i]=false;}
for(int i=1;i<=m;i++)
 if(sy[i]) {ly[i]+=Min;sy[i]=false;}
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans+=g[i][cx[i]];
return ans;
}
 int main() {
int T;
scanf("%d",&T);
 while(T--) {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&data[i][j]);
//build
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=n;k++)
g[i][(j-1)*n+k]=-data[i][j]*k;//花費
m=n*m;
double ans=-1.0*KM()/n;
printf("%.6f\n",ans);
}
return 0;
}
|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|