被网络流支配的一天;
具体概念以后再讲,先总结题目;
将石柱拆点,分为底端和顶端;
每个石柱底端顶端连边,容量为高度;
首先,超级源点向每个蜥蜴所在石柱的底部连一条为1的边;
然后,找到能跳出去的石柱,顶端向超级汇点连一条为inf的边;
对于地图内任意两个石柱,如果间距小于d,就将其中一根石柱的顶部与另一根石柱的底部相连,其连线容量为inf。
总数-最大流即可;
#include<bits/stdc++.h> using namespace std; #define N 10005 #define inf 1e9 template<typename T>inline void read(T &x) { x=0; register int f=1; register char ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } int n,m,d,s,t,tot=1,num,k; int lin[N],dep[N],X[N],Y[N],zn[1000][1000],hi[201][201]; char s2[201][201],s1[201][201]; struct gg { int flow,y,next; }a[500000]; inline void add(int x,int y,int flow) { a[++tot].y=y; a[tot].next=lin[x]; a[tot].flow=flow; lin[x]=tot; } queue<int> q; bool bfs() { memset(dep,0,sizeof(dep)); while(!q.empty()) q.pop(); dep[s]=1; q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int i=lin[u];i;i=a[i].next) { int v=a[i].y; if(!dep[v]&&a[i].flow) { dep[v]=dep[u]+1; q.push(v); if(v==t) return true; } } } return false; } int dinic(int x,int t,int limit) { if(x==t) return limit; int flow=0,k; for(int i=lin[x];i;i=a[i].next) { int v=a[i].y; if(dep[v]==dep[x]+1&&a[i].flow&&(k=dinic(v,t,min(limit,a[i].flow)))) { flow+=k; limit-=k; a[i].flow-=k; a[i^1].flow+=k; if(!flow) break; } } return flow; } int main() { int i,j,num=0; read(n); read(m); read(d); for(i=1;i<=n;i++) { scanf("%s",s1[i]); for(j=0;j<m;j++) { hi[i][j+1]=s1[i][j]-48; if(hi[i][j+1]!=0) k++,X[k]=i,Y[k]=j+1,zn[i][j+1]=k; } } for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(hi[i][j]!=0) { if(i<=d||i+d>n||j<=d||j+d>m) add(zn[i][j]+k,2*k+1,inf),add(2*k+1,zn[i][j]+k,0);//2*k+1为超级汇点,可以跳出去就连边 } for(i=1;i<=n;i++) { scanf("%s",s2[i]); for(j=0;j<m;j++) if(s2[i][j]=='L') { int v=zn[i][j+1]; num++; add(0,v,1); add(v,0,0); } } for(i=1;i<=k;i++) add(i,i+k,hi[X[i]][Y[i]]),add(i+k,i,0); for(i=1;i<=k;i++) for(j=1;j<=k;j++) { if(i==j) continue; if((X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j])<=d*d) add(i+k,j,inf),add(j,i+k,0); } s=0;t=2*k+1; int max_flow=0; while(bfs()) max_flow+=dinic(s,t,inf); printf("%d",num-max_flow); }
星际战争;
这个二分我真的;用的实数二分,都成上10000,最后除,题目允许误差;
很显然答案就有单调性,我们可以二分一个时间t,如果在t时间,伤害值为hurt*t,源点向武器连边,容量为伤害值,武器和装甲之间连边,容量为inf,装甲和汇点连边
容量为装甲值;跑最大流判定是否等于装甲值之和;
#include<bits/stdc++.h> using namespace std; #define inf 1e11 #define ll long long template<typename T>inline void read(T &x) { x=0; register int f=1; register char ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } ll n,m,tot=1,ss,tt; ll hp[60],hurt[60],lin[200],d[200],X[60][60]; struct gg { ll x,y,next,flow; }a[500001]; inline void add(ll x,ll y,ll flow) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].flow=flow; } inline void build(ll t) { for(int i=1;i<=m;i++) { add(ss,i,hurt[i]*t); add(i,ss,0); } for(int i=1;i<=n;i++) { add(i+m,tt,hp[i]); add(tt,i+m,0); } for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) if(X[i][j]) add(i,j+m,inf),add(j+m,i,0) ; } queue<int> q; inline bool bfs() { memset(d,0,sizeof(d)); while(!q.empty()) q.pop(); q.push(ss); d[ss]=1; while(q.size()) { int x=q.front(); q.pop(); for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(a[i].flow&&!d[y]) { d[y]=d[x]+1; q.push(y); if(y==tt) return true; } } } return false; } inline ll dinic(ll x,ll flow) { if(x==tt) return flow; ll rest=flow,k; for(int i=lin[x];i&&rest;i=a[i].next) { ll y=a[i].y; if(a[i].flow&&d[y]==d[x]+1) { k=dinic(y,min(rest,a[i].flow)); if(!k) d[y]=0; a[i].flow-=k; a[i^1].flow+=k; rest-=k; } } return flow-rest; } inline ll check() { ll max_flow=0; while(bfs()) max_flow+=dinic(ss,inf); return max_flow; } int main() { read(n); read(m); ll sum=0; ss=0,tt=n+m+1; for(int i=1;i<=n;i++) { read(hp[i]); hp[i]*=10000; sum+=hp[i]; } for(int i=1;i<=m;i++) { read(hurt[i]); } for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) read(X[i][j]); ll l=0,r=sum; while(l<r) { ll mid=(l+r)>>1; tot=1; memset(lin,0,sizeof(lin)); build(mid); ll ans=check(); if(ans>=sum) r=mid; else l=mid+1; } printf("%.6lf",l*1.0/10000); return 0; }
首先这是个最小割,最小割等于最大流;
任意一个同学和源点相连,容量为art[i][j],在和汇点相连,容量为science[i][j],求最小割,割掉理科容量即选择文科,文科也是;
那么考虑处理same_art/same_science的情况,我们可以新建一个节点,源点和它相连,容量为same_art,那么他需要和另外五个点,(与他相邻和它本身)相连,容量为inf。
因为走这条边一定要走另外五条;对于same_science,我们将其连向汇点,同样连五个点;
跑最大流,总数减去就好,注意反向边的容量为0;
#include<bits/stdc++.h> using namespace std; #define N 500001 #define inf 1e9 template<typename T>inline void read(T &x) { x=0; register int f=1; register char ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } int dx[]={0,0,0,1,-1}; int dy[]={0,-1,1,0,0}; int n,m,tot=1,ss,tt,lin[N],d[N],art[501][501],science[501][501],same_art[501][501],same_science[501][501]; struct gg { int y,next,flow; }a[2000001]; inline void add(int x,int y,int flow) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].flow=flow; } queue<int> q; inline bool bfs() { memset(d,0,sizeof(d)); while(!q.empty()) q.pop(); q.push(ss); d[ss]=1; while(q.size()) { int x=q.front(); q.pop(); for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(a[i].flow&&!d[y]) { d[y]=d[x]+1; q.push(y); if(y==tt) return true; } } } return false; } inline int dinic(int x,int flow) { if(x==tt) return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=a[i].next) { int y=a[i].y; if(a[i].flow&&d[y]==d[x]+1) { k=dinic(y,min(rest,a[i].flow)); if(!k) d[y]=0; a[i].flow-=k; a[i^1].flow+=k; rest-=k; } } return flow-rest; } inline int num(int i,int j) { return (i-1)*m+j; } int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); read(n); read(m); ss=0,tt=n*m+1; int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { read(art[i][j]); ans+=art[i][j]; add(ss,num(i,j),art[i][j]); add(num(i,j),ss,0); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { read(science[i][j]); ans+=science[i][j]; add(num(i,j),tt,science[i][j]); add(tt,num(i,j),0); } int cnt=tt; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { read(same_art[i][j]); ans+=same_art[i][j]; add(ss,++cnt,same_art[i][j]); add(cnt,ss,0); for(int k=0;k<5;k++) { int xx=i+dx[k],yy=j+dy[k]; if(xx<1||xx>n||yy<1||yy>m) continue; add(cnt,num(xx,yy),inf); add(num(xx,yy),cnt,0); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { read(same_science[i][j]); ans+=same_science[i][j]; add(++cnt,tt,same_science[i][j]); add(tt,cnt,0); for(int k=0;k<5;k++) { int xx=i+dx[k],yy=j+dy[k]; if(xx<1||xx>n||yy<1||yy>m) continue; add(num(xx,yy),cnt,inf); add(cnt,num(xx,yy),0); } } int max_flow=0,flow=0; while(bfs()) while(flow=dinic(ss,inf)) max_flow+=flow; printf("%d ",ans-max_flow); return 0; }
这个题目是让求可行边和必须边;
一般流程:不说证明;
dinic求最大流,tarjan跑强连通分量(忽略此时流量为0的边,我的理解是此时两边不连通,对于连通的定义就是两点之间的边还有容量,那么已经被阻隔,不能再走);
如果不是一个强连通分量,是可行边,在可行边里找必须边,当且仅当源点和x是一个强连通分量,y和汇点是一个强连通分量;
#include<bits/stdc++.h> using namespace std; #define N 500001 #define inf 1e9 template<typename T>inline void read(T &x) { x=0; register int f=1; register char ch=getchar(); while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar(); x*=f; } int n,m,s,t,tot=1,num,top,cnt; int lin[N],ins[N],dfn[N],low[N],c[N],Stack[N],d[N]; struct gg { int x,y,next,flow; }a[N<<1]; inline void add(int x,int y,int flow) { a[++tot].y=y; a[tot].x=x; a[tot].flow=flow; a[tot].next=lin[x]; lin[x]=tot; } queue<int> q; inline bool bfs() { memset(d,0,sizeof(d)); while(!q.empty()) q.pop(); q.push(s); d[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; if(a[i].flow&&!d[y]) { q.push(y); d[y]=d[x]+1; if(y==t) return 1; } } } return 0; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=a[i].next) { int y=a[i].y; if(a[i].flow&&d[y]==d[x]+1) { k=dinic(y,min(rest,a[i].flow)); if(!k) d[y]=0; a[i].flow-=k; a[i^1].flow+=k; rest-=k; } } return flow-rest; } void tarjan(int x) { dfn[x]=low[x]=++num; Stack[++top]=x; ins[x]=1; for(int i=lin[x];i;i=a[i].next) { int u=a[i].y; if(!a[i].flow) continue; if(!dfn[u]) { tarjan(u); low[x]=min(low[x],low[u]); } else if(ins[u]) low[x]=min(low[x],dfn[u]); } int k; if(low[x]==dfn[x]) { ++cnt; do { k=Stack[top--]; ins[k]=0; c[k]=cnt; }while(x!=k); } } int main(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); read(n); read(m); read(s); read(t); for(int i=1,x,y,w;i<=m;i++) { read(x); read(y); read(w); add(x,y,w); add(y,x,0); } int flow=0,max_flow=0; while(bfs()) while(flow=dinic(s,inf)) max_flow+=flow; for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i); } for(int i=2;i<tot;i+=2) { int x=a[i].x,y=a[i].y; if(!a[i].flow&&c[x]!=c[y]) { printf("1 "); if(c[s]==c[x]&&c[y]==c[t]) printf("1 "); else printf("0 "); } else printf("0 0 "); } return 0; }
今天没写费用流,而且上下界网络流也没写,有空补上;