A.蜥蜴
将每个石柱拆点,最多可起跳石柱的高度次,入点向出点连其高度的边。
两个石柱平面距离不超过d时可互相到达,由一个石柱的出点向另一个石柱的入点连$Inf$边。
建立源点和汇点。
当前石柱有蜥蜴时,源点$s$向该石柱入点连容量为$1$的边。
当前石柱的横或纵坐标距离边界最短距离$<d$时,该石柱出点向汇点$t$连$Inf$边。
跑最大流就是可逃出去最多的蜥蜴数,用总蜥蜴数减去即可。
#include <bits/stdc++.h> #define Inf 0x3f3f3f3f using namespace std; int n,m,d,S,t,ans; char s[25]; const int maxn=805; int cnt=-1,head[maxn],vis[maxn]; struct Edge{ int to,next,w; }e[maxn*maxn]; void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt; } bool Judge(int x,int y,int xx,int yy) { double temp=sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy)); return temp<=d; } queue<int>q; bool bfs() { q.push(S); memset(vis,0,sizeof(vis)); vis[S]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&!vis[v]) { vis[v]=vis[u]+1; q.push(v); } } } return vis[t]; } int dfs(int u,int flow) { if(u==t) return flow; int temp=0,w; for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&vis[v]==vis[u]+1) { w=dfs(v,min(flow-temp,e[i].w)); e[i].w-=w,e[i^1].w+=w,temp+=w; if(temp==flow) return temp; } } if(!temp) vis[u]=0; return temp; } int dinic() { int res=0; while(bfs()) res+=dfs(S,Inf); return res; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&d); S=0,t=n*m*2+1; for(int i=1;i<=n;++i) { scanf("%s",s+1); for(int j=1;j<=m;++j) { if(s[j]!='0') add((i-1)*m+j,(i-1)*m+j+n*m,s[j]-'0'); } } for(int i=1;i<=n;++i) { scanf("%s",s+1); for(int j=1;j<=m;++j) { if(s[j]=='L') add(S,(i-1)*m+j,1),ans++; } } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(i-d<1||i+d>n||j-d<1||j+d>m) add((i-1)*m+j+n*m,t,Inf); } } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { for(int u=1;u<=n;++u) { for(int v=1;v<=m;++v) { if(Judge(i,j,u,v)) add((i-1)*m+j+n*m,(u-1)*m+v,Inf); } } } } printf("%d ",ans-dinic()); return 0; }
B.星际战争
每个激光武器向可攻击的机器人连一条$Inf$边。
机器人向汇点连自己的装甲值。
二分答案 每次二分需要的时间$t$,重建汇点向每个激光武器连$B[i] imes t$容量的边,检验最后$A[i]$是否满流。
处理精度问题可用$long double$,或者因为题目要求精度为$10^{-3}$可将其$ imes 1000$,最后输出答案时除回去即可。
邻接表清空时不仅$cnt$和$head$数组要初始化,链表$e$也要重置。
#include<bits/stdc++.h> #define Inf 0x3f3f3f3f3f3f3f3f using namespace std; const int maxn=50+5; int n,m,a[maxn],b[maxn],s,t; bool mapp[maxn][maxn]; int cnt=-1,head[maxn<<1],vis[maxn<<1]; typedef long long ll; ll sum; struct Edge{ int to,next; ll w; }e[maxn*maxn<<1]; void add(int u,int v,ll w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt; } queue<int>q; bool bfs() { q.push(s); memset(vis,0,sizeof(vis)); vis[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&!vis[v]) { vis[v]=vis[u]+1; q.push(v); } } } return vis[t]; } ll dfs(int u,ll flow) { if(u==t) return flow; ll temp=0,w; for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&vis[v]==vis[u]+1) { w=dfs(v,min(flow-temp,e[i].w)); e[i].w-=w,e[i^1].w+=w,temp+=w; if(flow==temp) return flow; } } if(!temp) vis[u]=0; return temp; } ll dinic() { ll res=0; while(bfs()) res+=dfs(s,Inf); return res; } bool Judge(ll mid) { cnt=-1; memset(head,-1,sizeof(head)); memset(e,0,sizeof(e)); for(int i=1;i<=n;++i) add(i+m,t,a[i]); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) if(mapp[i][j]) add(i,j+m,Inf); for(int i=1;i<=m;++i) add(s,i,mid*b[i]); return dinic()>=sum; } int main() { scanf("%d%d",&n,&m); s=0,t=n+m+1; for(int i=1;i<=n;++i) scanf("%d",&a[i]),a[i]=a[i]*1000,sum+=a[i]; for(int i=1;i<=m;++i) scanf("%d",&b[i]); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) scanf("%d",&mapp[i][j]); ll l=0,r=5e9,mid; while(l<=r) { mid=l+r>>1LL; if(Judge(mid)) r=mid-1; else l=mid+1; } printf("%lf ",(double)l/1000); return 0; }
D.士兵占领
源点连行坐标,汇点连纵坐标。
反向思考,边容量设置为其最多可不放置的士兵数,假设最初所有能放置士兵的位置先都放置士兵。
行列无障碍,建立一条容量为$1$的边。
跑出的最大流就是在符合题意的前提下,最多能不放置的士兵数。
#include <bits/stdc++.h> #define Inf 0x3f3f3f3f using namespace std; const int maxn=100+5; int m,n,k,s,t,hang[maxn],lie[maxn],x,y; bool mapp[maxn][maxn]; int cnt=-1,head[maxn<<1],vis[maxn<<1]; struct Edge{ int to,next,w; }e[maxn*maxn<<1]; void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt; } queue<int>q; bool bfs() { memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&!vis[v]) { vis[v]=vis[u]+1; q.push(v); } } } return vis[t]; } int dfs(int u,int flow) { if(u==t) return flow; int temp=0,w; for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&vis[v]==vis[u]+1) { w=dfs(v,min(flow-temp,e[i].w)); e[i].w-=w,e[i^1].w+=w,temp+=w; if(temp==flow) return temp; } } if(!temp) vis[u]=0; return temp; } int dinic() { int res=0; while(bfs()) res+=dfs(s,Inf); return res; } int main() { s=0,t=n+m+1; memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;++i) scanf("%d",&hang[i]),hang[i]=m-hang[i]; for(int i=1;i<=m;++i) scanf("%d",&lie[i]),lie[i]=n-lie[i]; int flag=0; for(int i=1;i<=k;++i) { scanf("%d%d",&x,&y); mapp[x][y]=1; hang[x]--,lie[y]--; if(hang[x]<0||lie[x]<0) flag=1; } if(flag) printf("JIONG! "); else { for(int i=1;i<=n;++i) add(s,i,hang[i]); for(int i=1;i<=m;++i) add(i+n,t,lie[i]); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(!mapp[i][j]) add(i,j+n,1); } } } printf("%d ",n*m-k-dinic()); return 0; }
H.最大获利
最大权闭合子图简单模型。
每个用户群设置一个节点,源点$s$向其连可获得收益为$w[i]$的边,节点向两个所需的中转站连$Inf$边。
每个中转站向汇点$t$连建立它所需的费用$p[i]$的边。
初始设置服务所有用户群,$sum=$获利$w[i]$的总和,在上图中跑最小割。
割边不会为$Inf$边,那么就只会为$w[i]$或$p[i]$的边:
割$w[i]$时代表失去该用户群;
反之保留该用户群,花费对应的$p[i]$建立中转站。
答案为$sum-$最小割。
#include <bits/stdc++.h> #define Inf 0x3f3f3f3f using namespace std; const int maxn=6e4+5; int n,m,x,y,z,s,t,ans; int cnt=-1,head[maxn],vis[maxn]; struct Edge{ int to,next,w; }e[maxn*6]; void add(int u,int v,int w) { e[++cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt; } queue<int>q; bool bfs() { memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&!vis[v]) { vis[v]=vis[u]+1; q.push(v); } } } return vis[t]; } int dfs(int u,int flow) { if(u==t) return flow; int temp=0,w; for(int i=head[u];~i;i=e[i].next) { int v=e[i].to; if(e[i].w&&vis[v]==vis[u]+1) { w=dfs(v,min(flow-temp,e[i].w)); e[i].w-=w,e[i^1].w+=w,temp+=w; if(flow==temp) return temp; } } if(!temp) vis[u]=0; return temp; } int dinic() { int res=0; while(bfs()) res+=dfs(s,Inf); return res; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); s=0,t=n+m+1; for(int i=1;i<=n;++i) scanf("%d",&x),add(i,t,x); for(int i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),add(i+n,x,Inf),add(i+n,y,Inf),add(s,i+n,z),ans+=z; printf("%d ",ans-dinic()); return 0; }