1. 程式人生 > 實用技巧 >2020/10/23 模擬賽 chip

2020/10/23 模擬賽 chip

Description

一張$n \times n$的晶片上,有些格子己經焊有零件,有些格子禁止焊接零件。問,在同時滿足下面三個約束的前提下,至多可以再焊接多少個零件。

1. 每個格子至多焊接一個零件
2. 第$i$行的零件總數和第$i$列一致
3. 任意一行的零件數不得超過總零件數的$\frac AB$

Solution

帶負環的費用流

列舉總零件數的$\frac AB$,建圖跑費用流,設$hangnum$為第$i$行一開始就有的零件數

  1. 從第$i$行向第$i$列連邊,容量為$lim-hangnum_i$,費用為$0$
  2. 若$hangnum > lienum$,從$S$連向第$i$列,容量為$delta$,費用為$0$
  3. 若$hangnum < lienum$,從第$i$行連向$T$,容量為$delta$,費用為$0$
  4. 若$(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; }
chip