牛客练习赛89

F.牛牛防疫情

牛牛用卖烤串赚的钱买了一款游戏,这款游戏的地图是一个 n*n 的网格,其中有 m 个地区存在感染源(红色),其余地区为安全区(白色)。已知一个感染源可同时将与其相邻(上下左右)的安全区感染,被感染的安全区称之为新发地(黄色)。一个安全区变为一个新发地需要付出大小为 c 的代价。新发地可在下一时刻作为感染源将与其相邻的安全区感染为新的新发地。
为了遏制疫情扩散,牛牛决定采取下列两种措施
1. 在感染源(新发地)周围修建墙,墙可阻止疫情的扩散。但每堵墙需要付出大小为 1 的代价。
2. 任意让疫情扩散。
牛牛现在想知道采取哪种措施可以使得付出的代价最小。
思路
所有点向汇点连边流量是 表示被感染,然后本身被感染的点连向源点流量 INF 表示被感染。然后点和它四边之间连边流量是 表示要建护栏。
然后跑最小割就是答案。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int INF=0x3f3f3f3f;
const int N=4e5+5,M=4e5+5;
int n,m,c,s,t,k,head[N],nex[M],to[M],tot,mp[205][205];
ll w[M],maxflow,deep[N];//层级数,其实应该是level
int now[M];//当前弧优化
queue<int>q;
inline void add(int x,int y,int z){//建正边和反向边
    to[tot] = y;w[tot] = z;nex[tot] = head[x];head[x] = tot ++ ;
    to[tot] = x;w[tot] = 0;nex[tot] = head[y];head[y] = tot ++ ;
}
inline bool bfs(){//在残量网络中构造分层图
//注意这个n,应该是实际的总点数,加上S和T的总点数,这里很容易出错
    for(int i = s; i <= t; ++ i)
    	deep[i] = INF;
    while(!q.empty())q.pop();
    q.push(s);deep[s] = 0;now[s] = head[s];//一些初始化
    while(!q.empty()){
        int x = q.front();q.pop();
        for(int i = head[x];~i;i = nex[i]){
            int y = to[i];
            if(w[i] > 0 && deep[y] == INF){//没走过且剩余容量大于0
                q.push(y);
                now[y] = head[y];//先初始化,暂时都一样
                deep[y] = deep[x] + 1;
                if(y == t)return 1;//找到了
            }
        }
    }
    return 0;
}
//flow是整条增广路对最大流的贡献,rest是当前最小剩余容量,用rest去更新flow
ll dfs(int x,ll flow){//在当前分层图上增广
    if(x == t)return flow;
    ll ans = 0,k,i;
    for(i = now[x];~i && flow;i = nex[i]){
        now[x] = i;//当前弧优化(避免重复遍历从x出发的不可拓展的边)
        int y = to[i];
        if(w[i] > 0 && (deep[y] == deep[x] + 1)){//必须是下一层并且剩余容量大于0
            k = dfs(y,min(flow,w[i]));//取最小
            if(!k)deep[y] = INF;//剪枝,去掉增广完毕的点
            w[i] -= k;//回溯时更新
            w[i ^ 1] += k;//成对变换
            ans += k;
            flow -= k;
        }
        //if(!flow)return rest;
    }
    return ans;
}
void dinic(){
    while(bfs())
        maxflow += dfs(s,INF);
}
int get(int x,int y){
	return (x-1)*n+y;
}
int main(){
    scanf("%d%d%d",&n,&m,&c);
    memset(head, -1, sizeof head);
    s= 0,t= n*n+1;
    for(int i=1,x,y,z;i<=m;i++){
    	cin>>x>>y;
    	x++,y++;
		add(s,get(x,y),INF);//s1
    }
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		add(get(i,j),t,c);
    	//	if(mp[i][j])continue;
    		if(i!=n)add(get(i,j),get(i+1,j),1);
    		if(i!=1)add(get(i,j),get(i-1,j),1);
    		if(j!=n)add(get(i,j),get(i,j+1),1);
    		if(j!=1)add(get(i,j),get(i,j-1),1);
		}
	}
    dinic();
    printf("%lld\n",maxflow-c*m);
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值