POJ-3686 The Windy's 最小权匹配
题目链接:http://poj.org/problem?id=3686
这个题目容易想到拆点,开始我是直接拆点在求的,考虑到直接建图复杂度有点高,然后就在找增广路的同时再加点,因为并不是每个点都会达到50个,这样实际复杂度会好很多。敲完之后,样例过了,然后直接叫,WA!后来看了discuss顿然醒悟,找增光路的时候,之前的状态改变了,那么后面的状态也会改变,那么那样求下去就不一定是最优解!所有要保证所有的匹配之间是互补影响的!假设某个工作坊要处理K个命令,时间分别是t1,t2......tk,那么总共的时间就是k*t1+(k-1)*t2+......+tk。发现没有,如果k表示的是倒数第k个命令,那么总时间变为了:t1+2*t2+......+k*tk。所以我们拆点后再建图的时候,边权值w[i][j]*k,(1<=k<=50),那么直接求最小权匹配就行了。那么我们看下时间复杂度,匹配n个点O(n),每个点至多找n次增广路O(n),邻接矩阵的匈牙利O(n^2),总共就是O(50*50*2500*2500),这是理论的时间复杂度,但实际上远远没有这么可怕,因为很难达到最坏情况,而且顶标的维护使得匈牙利算法效率大大的高,所以一般就算O(n^4)的KM算法也够用了。我的代码POJ跑了47ms,这也出乎了我的意料,总感觉POJ的题目数据很弱啊!!!
1 //STATUS:G++_AC_47MS_1344KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 using namespace std; 13 #define LL long long 14 #define Max(a,b) ((a)>(b)?(a):(b)) 15 #define Min(a,b) ((a)<(b)?(a):(b)) 16 #define mem(a,b) memset(a,b,sizeof(a)) 17 #define lson l,mid,rt<<1 18 #define rson mid+1,r,rt<<1|1 19 const int MAX=55,INF=0x3f3f3f3f; 20 21 int w[MAX][MAX*MAX],S[MAX],T[MAX*MAX],lx[MAX],ly[MAX*MAX],y[MAX*MAX]; 22 int Test,n,m,slack; 23 24 int dfs(int u) 25 { 26 int v,t; 27 S[u]=1; 28 for(v=0;v<m;v++){ 29 t=w[u][v]-lx[u]-ly[v]; 30 if(!t){ 31 if(!T[v]){ 32 T[v]=1; 33 if(y[v]==-1 || dfs(y[v])){ 34 y[v]=u; 35 return 1; 36 } 37 } 38 } 39 else if(t<slack)slack=t; 40 } 41 return 0; 42 } 43 44 double KM() 45 { 46 int i,j; 47 double s; 48 mem(ly,0); 49 mem(y,-1); 50 for(i=0;i<n;i++){ 51 lx[i]=INF; 52 for(j=0;j<m;j++) 53 if(w[i][j]<lx[i])lx[i]=w[i][j]; 54 } 55 for(i=0;i<n;i++){ 56 while(1){ 57 slack=INF; 58 mem(S,0);mem(T,0); 59 if(dfs(i))break; 60 for(j=0;j<n;j++)if(S[j])lx[j]+=slack; 61 for(j=0;j<m;j++)if(T[j])ly[j]-=slack; 62 } 63 } 64 for(i=s=0;i<m;i++) 65 if(y[i]!=-1)s+=w[y[i]][i]; 66 return s; 67 } 68 69 int main() 70 { 71 // freopen("in.txt","r",stdin); 72 int i,j,k,t; 73 double ans; 74 scanf("%d",&Test); 75 while(Test--) 76 { 77 scanf("%d%d",&n,&m); 78 for(i=0;i<n;i++) 79 for(j=0;j<m;j++) 80 scanf("%d",&w[i][j]); 81 for(i=0,t=m;i<t;i++) 82 for(j=2;j<=50;j++,m++) 83 for(k=0;k<n;k++) 84 w[k][m]=w[k][i]*j; 85 ans=KM(); 86 87 printf("%.6f\n",ans/n); 88 } 89 return 0; 90 }