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 }
View Code

费用流

  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 }
View Code

好像还是KM跑的快一点而且代码短一点

 

posted @ 2018-07-23 19:31  wawawa8  阅读(120)  评论(0编辑  收藏  举报