JOJ 2662: 採購方案 (二分答案+多重匹配)
CCST@JLU 要舉辦這次的程式設計大賽。為了節省開支,在各方面都得考慮仔細,比賽場地布置也不例外。在布置場地時,需要為每台電腦提供電源,由於電腦和電源的位置已經固定,所以要採購一些電源線來串連電腦和電源。kkk在採購時發現,購買相同數量的等長電源線總比購買長度不一的電源線要便宜很多(雖然電源線的價格和長度成正比,卻不影響這個規律:)),所以kkk決定購買相同長度的電源線來布置場地。kkk想節省開支,你能協助他嗎?
題解 :二分答案 + 多重匹配,枚舉答案,對小於等於枚舉數的邊加入到網路中看是否可行,可行就繼續找比它小的解 , 不可行就找大一點的解。
多重匹配流的構圖,很好想 , 將可多詞匹配的點與源匯的連邊的權值更改成可匹配的次數即可。
#include <cstdio>#include <cstring>#define max(a,b) (a>b?a:b)#define min(a,b) (a<b?a:b)const int maxn=800;const int inf = 1<<28;const int s=0 ;struct Edge { int v,next,w;}edge[maxn*maxn];int head[maxn],cnt;//for sapvoid addedge(int u , int v , int w){ edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++;}int n,m,cc;///int sap(int t){ int pre[maxn],cur[maxn]; int dis[maxn],gap[maxn]; int flow=0 , aug=inf ,u; bool flag; for (int i=0 ; i<=t ; ++i) { cur[i]=head[i]; gap[i]=dis[i]=0; } gap[s]=t+1; u=pre[s]=s; while (dis[s]<=t) { flag=0 ; for (int &j=cur[u] ; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[u]==dis[v]+1) { flag=1; if(edge[j].w<aug)aug=edge[j].w; pre[v]=u; u=v; if (u==t) { flow+=aug; while (u!=s) { u=pre[u]; edge[cur[u]].w-=aug; edge[cur[u]^1].w+=aug; } aug=inf; } break; } } if (flag)continue ; int mindis=t+1; for (int j=head[u]; ~j ; j=edge[j].next) { int v=edge[j].v; if (edge[j].w>0 && dis[v]<mindis) { mindis=dis[v]; cur[u]=j; } } if(--gap[dis[u]]==0)break; gap[dis[u]=mindis+1]++; u=pre[u]; } return flow;}int map[maxn][maxn];int c[maxn];int main (){ int cas; scanf("%d",&cas); while (cas--) { scanf("%d%d%d",&n,&m,&cc); for (int i=0 ; i<n ; ++i) { for (int j=0 ; j<m ; ++j) { scanf("%d",map[i]+j); } } int sum=0; for (int i=0 ; i<m ; ++i) { scanf("%d",c+i); sum+=c[i]; } if(sum<n){puts("-1"); continue;} int ans=inf; int l=0,r=10000; /// while (l<=r) { int mid=(l+r)>>1; memset (head , -1 , sizeof(head)); cnt=0; for (int i=0 ; i<n ; ++i) { addedge (0 , i+1 , 1); for (int j=0 ; j<m ; ++j) { if(map[i][j]<=mid) { addedge(i+1,j+n+1,1); } } } for (int j=0 ; j<m ; ++j) addedge(j+n+1 , n+m+1 , c[j]); int match; match=sap(n+m+1); //printf("match = %d\n",match); if(n==match) { r=mid-1; ans=min(ans,mid); } else l=mid+1; } printf("%d\n",ans*cc*n); } return 0;}