网络流

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\n",ans-dinic());
    return 0;
}
View Code

 

B.星际战争

每个激光武器向可攻击的机器人连一条$Inf$边。

机器人向汇点连自己的装甲值。

二分答案 每次二分需要的时间$t$,重建汇点向每个激光武器连$B[i]\times t$容量的边,检验最后$A[i]$是否满流。

处理精度问题可用$long double$,或者因为题目要求精度为$10^{-3}$可将其$\times 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\n",(double)l/1000);
    return 0;
}
View Code

 

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!\n");
    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",n*m-k-dinic());
    return 0;
}
View Code

 

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\n",ans-dinic());
    return 0;
}
View Code

 

posted @ 2020-11-25 14:21  Kaiser_Kell  阅读(134)  评论(2编辑  收藏  举报