bzoj 4819 [Sdoi2017]新生舞会
题面
https://www.lydsy.com/JudgeOnline/problem.php?id=4819
题解
这题很好想 首先二分答案 然后令w[i][j]=a[i][j]-b[i][j]*nwans
那么只要判断能否找出一个完美匹配 使得
KM算法或者费用流都可以做
Code
KM算法
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 ll read(){ 6 ll x=0,f=1;char c=getchar(); 7 while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} 8 while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();} 9 return x*f; 10 } 11 12 const int maxn=110; 13 double w[maxn][maxn]; //边权 14 double la[maxn],lb[maxn]; //左右点的顶标 15 bool va[maxn],vb[maxn]; //访问标记(是否在交错树中) 16 int match[maxn]; //右部点匹配了哪一个左部点 17 int n; 18 double delta; 19 20 bool dfs(int x){ 21 va[x]=1; 22 for(int y=1;y<=n;y++) 23 if(!vb[y]) 24 if(fabs(la[x]+lb[y]-w[x][y])<=1e-8){ //相等子图 25 vb[y]=1; 26 if(!match[y] || dfs(match[y])){ 27 match[y]=x; 28 return 1; 29 } 30 } 31 else delta=min(delta,la[x]+lb[y]-w[x][y]); 32 return 0; 33 } 34 35 double KM(){ 36 memset(match,0,sizeof(match)); 37 for(int i=1;i<=n;i++){ 38 la[i]=-(1<<30); 39 lb[i]=0; 40 for(int j=1;j<=n;j++) 41 la[i]=max(la[i],w[i][j]); 42 } 43 for(int i=1;i<=n;i++) 44 while(1){ 45 memset(va,0,sizeof(va)); 46 memset(vb,0,sizeof(vb)); 47 delta=1<<30; 48 if(dfs(i)) break; 49 for(int j=1;j<=n;j++){ 50 if(va[j]) la[j]-=delta; 51 if(vb[j]) lb[j]+=delta; 52 } 53 } 54 double ans=0; 55 for(int i=1;i<=n;i++) 56 ans+=w[match[i]][i]; 57 return ans; 58 } 59 60 int a[maxn][maxn],b[maxn][maxn]; 61 62 int main(){ 63 #ifdef LZT 64 freopen("in","r",stdin); 65 #endif 66 67 n=read(); 68 for(int i=1;i<=n;i++) 69 for(int j=1;j<=n;j++) 70 a[i][j]=read(); 71 for(int i=1;i<=n;i++) 72 for(int j=1;j<=n;j++) 73 b[i][j]=read(); 74 double l=0,r=1e4; 75 while(r-l>(1e-8)){ 76 double md=(l+r)/2; 77 for(int i=1;i<=n;i++) 78 for(int j=1;j<=n;j++) 79 w[i][j]=a[i][j]-b[i][j]*md; 80 if(KM()>=0) l=md; 81 else r=md; 82 // cout<<l<<' '<<r<<endl; 83 } 84 printf("%.6f\n",l); 85 return 0; 86 }
费用流
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 ll read(){ 6 ll x=0,f=1;char c=getchar(); 7 while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} 8 while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();} 9 return x*f; 10 } 11 12 const int maxn=220,maxm=30200; 13 int to[maxm],edge[maxm],nxt[maxm],head[maxn]; 14 double cost[maxm]; 15 double d[maxn]; 16 int pre[maxn],v[maxn],incf[maxn]; 17 //最短路长度,最小剩余容量,当前点在最短路中的前驱,是否访问过 18 int n,k,tot,s,t,mxfl; 19 double ans; 20 21 void add(int x,int y,int cap,double c){ 22 to[++tot]=y,edge[tot]=cap,cost[tot]=c,nxt[tot]=head[x],head[x]=tot; 23 to[++tot]=x,edge[tot]=0,cost[tot]=-c,nxt[tot]=head[y],head[y]=tot; 24 } 25 26 bool spfa(){ 27 queue<int> q; 28 memset(d,0xcf,sizeof(d)); //-INF 29 memset(v,0,sizeof(v)); 30 q.push(s); 31 d[s]=0,v[s]=1; 32 incf[s]=1<<30; 33 while(!q.empty()){ 34 int x=q.front();q.pop(); 35 v[x]=0; 36 for(int i=head[x];i;i=nxt[i]){ 37 if(edge[i]==0) continue; //容量为0 38 int y=to[i]; 39 if(d[y]<d[x]+cost[i]){ 40 d[y]=d[x]+cost[i]; //这里是最大费用最大流 41 incf[y]=min(incf[x],edge[i]); 42 pre[y]=i; //记录前驱,方便递归找最短路 43 if(!v[y]) v[y]=1,q.push(y); 44 } 45 } 46 } 47 if(d[t]==d[t+1]) return false; 48 return true; 49 } 50 51 //更新最长增广路及其反向边的剩余容量 52 void update(){ 53 int x=t; 54 while(x!=s){ 55 int ind=pre[x]; 56 edge[ind]-=incf[t]; 57 edge[ind^1]+=incf[t]; 58 x=to[ind^1]; 59 } 60 mxfl+=incf[t]; 61 ans+=d[t]*incf[t]; 62 } 63 64 double calc(){ 65 while(spfa()) update(); 66 return ans; 67 } 68 69 int a[110][110],b[110][110]; 70 double w[110][110]; 71 72 int main(){ 73 #ifdef LZT 74 freopen("in","r",stdin); 75 #endif 76 n=read(); 77 for(int i=1;i<=n;i++) 78 for(int j=1;j<=n;j++) 79 a[i][j]=read(); 80 for(int i=1;i<=n;i++) 81 for(int j=1;j<=n;j++) 82 b[i][j]=read(); 83 s=n+n+1,t=n+n+2; 84 double l=0,r=1e4; 85 while(r-l>(1e-8)){ 86 double md=(l+r)/2; 87 for(int i=1;i<=n;i++) 88 for(int j=1;j<=n;j++) 89 w[i][j]=a[i][j]-b[i][j]*md; 90 tot=1; 91 memset(head,0,sizeof(head)); 92 memset(nxt,0,sizeof(nxt)); 93 for(int i=1;i<=n;i++) 94 add(s,i,1,0),add(i+n,t,1,0); 95 for(int i=1;i<=n;i++) 96 for(int j=1;j<=n;j++) 97 add(i,j+n,1,w[i][j]); 98 mxfl=ans=0; 99 calc(); 100 if(ans>=1e-8) l=md; 101 else r=md; 102 } 103 printf("%.6f\n",l); 104 return 0; 105 }
好像还是KM跑的快一点而且代码短一点