2020/10/23 模拟赛 chip
Description
一张$n \times n$的芯片上,有些格子己经焊有零件,有些格子禁止焊接零件。问,在同时满足下面 三个约束的前提下,至多可以再焊接多少个零件。
1. 每个格子至多焊接一个零件
2. 第$i$行的零件总数和第$i$列一致
3. 任意一行的零件数不得超过总零件数的$\frac AB$
Solution
带负环的费用流
枚举总零件数的$\frac AB$,建图跑费用流,设$hangnum$为第$i$行一开始就有的零件数
- 从第$i$行向第$i$列连边,容量为$lim-hangnum_i$,费用为$0$
- 若$hangnum > lienum$,从$S$连向第$i$列,容量为$delta$,费用为$0$
- 若$hangnum < lienum$,从第$i$行连向$T$,容量为$delta$,费用为$0$
- 若$(i,j)$可以放置,从第$j$列连向第$i$行,容量为$1$,费用为$0$
最小费用最大流的相反数即为答案
但是会出现负环,所以要先走一遍负环
将原图中每个点拆成入点和出点,对应的每一条边在新图中由入点连向出点,可以保证所有负环不再组成负环,被拆分成多条独立的路径,原来的路径一定不在最大流中、
在新图中跑最小费用最大流就可以计算所有负环的费用,再对应着将流量改回原图,在该残余网络上跑最小费用最大流就不会陷入死循环
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<cmath> using namespace std; int n,A,B,delta[45],hangcan[45],liecan[45],hangnum[45],lienum[45],total,ans=-1,cnt; const int INF=0x7f7f7f7f; char map[45][45]; struct Edge { int head[355],nxt[50005],to[50005],w[50005],dis[50005],sta[50005],dist[355],tot,S,T; bool vst[355]; void clear() { memset(head,0,sizeof(head)); tot=1; } void addedge(int u,int v,int c,int d) { nxt[++tot]=head[u]; head[u]=tot; to[tot]=v; sta[tot]=u; w[tot]=c; dis[tot]=d; nxt[++tot]=head[v]; head[v]=tot; to[tot]=u; sta[tot]=v; w[tot]=0; dis[tot]=-d; } bool SPFA(int s,int t) { memset(dist,127,sizeof(dist)); memset(vst,false,sizeof(vst)); queue<int>q; vst[s]=true; dist[s]=0; q.push(s); while(q.size()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(w[i]>0&&dist[v]>dist[u]+dis[i]) { dist[v]=dist[u]+dis[i]; if(!vst[v]) { vst[v]=true; q.push(v); } } } vst[u]=false; } return dist[t]<INF; } int DFS(int s,int flow,int t) { if(s==t||flow<=0) { return flow; } int rest=flow; vst[s]=true; for(int i=head[s];i;i=nxt[i]) { int v=to[i]; if(w[i]>0&&dist[s]+dis[i]==dist[v]&&!vst[v]) { int k=DFS(v,min(rest,w[i]),t); rest-=k; w[i]-=k; w[i^1]+=k; if(rest<=0) { break; } } } return flow-rest; } void ZKW(int &costs) { while(SPFA(S,T)) { memset(vst,false,sizeof(vst)); costs+=dist[T]*DFS(S,INF,T); } } bool check() { for(int i=head[S];i;i=nxt[i]) { if(!(i&1)&&w[i]) { return false; } } return true; } }G1,G2; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } int X(int a,int t) { return a*2+t+1; } int xiaoquan() { G2.T=320; for(int i=2;i<=G1.tot;i+=2) { G2.addedge(X(G1.sta[i],0),X(G1.to[i],1),G1.w[i],G1.dis[i]); } for(int i=0;i<=G1.T;i++) { if((i>n&&i<100)||(i>100+n&&i<G1.T)) { continue; } G2.addedge(G2.S,X(i,0),INF,0); G2.addedge(X(i,1),G2.T,INF,0); G2.addedge(X(i,0),X(i,1),INF,0); } int costs=0; G2.ZKW(costs); for(int i=2;i<=G1.tot;i+=2) { int del=G1.w[i]-G2.w[i]; G1.w[i]-=del; G1.w[i^1]+=del; } return -costs; } void calc(int lim) { G1.clear(); G2.clear(); for(int i=1;i<=n;i++) { if(lienum[i]>lim||hangnum[i]>lim) { return; } } G1.S=G2.S=0; G1.T=G2.T=153; for(int i=1;i<=n;i++) { G1.addedge(i,i+100,lim-hangnum[i],0); if(delta[i]>0) { G1.addedge(G1.S,i+100,delta[i],0); } else if(delta[i]<0) { G1.addedge(i+100,G1.T,-delta[i],0); } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(map[i][j]=='.') { G1.addedge(j+100,i,1,-1); } } } int ttt=xiaoquan(),costs=0; G1.ZKW(costs); if(!G1.check()) { return; } ttt-=costs; if((double)(ttt+cnt)*A/B>=lim) { ans=max(ans,ttt); } } int main() { n=read(); A=read(); B=read(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { map[i][j]=getchar(); if(map[i][j]=='C') { ++total; ++cnt; ++delta[i]; --delta[j]; ++hangnum[i]; ++lienum[j]; ++hangcan[i]; ++liecan[j]; } else if(map[i][j]=='.') { ++total; ++hangcan[i]; ++liecan[j]; } } getchar(); } for(int i=1;i<=n;i++) { if(hangnum[i]>(double)total*A/B||min(hangcan[i],liecan[i])<max(hangnum[i],lienum[i])) { puts("impossible"); return 0; } } for(int i=n;i>=1;i--) { calc(i); if(ans!=-1) { break; } } if(ans==-1) { for(int i=1;i<=n;i++) { if(delta[i]||hangnum[i]>(double)cnt*A/B) { puts("impossible"); return 0; } } puts("0"); return 0; } printf("%d\n",ans); return 0; }