• 【洛谷3355】骑士共存问题(网络流)


    点此看题面

    • 给定一张(n imes n)的棋盘,其中有(m)个障碍物。
    • 问在互不攻击的前提下最多能放多少匹马。
    • (nle200)

    二分图最小割模型

    这道题目的关键是在于选了一个位置,就不能再选择它能攻击到的位置。

    但发现会互相攻击的两个位置必然满足奇偶性不同,也就是说我们可以建出一张二分图。

    根据经典的最小割模型,我们从超级源向所有黑点连一条容量为(1)的边,从所有白点向超级汇连一条容量为(1)的边,并在能相互攻击的黑点和白点之间连一条容量为(INF)的边。(注意,有障碍物的点不连任何边)

    规定一条边选择了就表示不选择对应的位置,这也就很好理解为什么我们在中间连容量为(INF)的边,其实就表示无法割断。

    根据最小割=最大流,我们只要跑一遍最大流,就能求出最少不选多少个位置。

    因此答案就是总点数((n imes n))减去原本规定的障碍格数((m))减去最大流。

    代码:(O(Dinic))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200
    #define INF (int)1e9
    using namespace std;
    int n,m,p[N+5][N+5];const int dx[8]={1,1,2,2,-1,-1,-2,-2},dy[8]={2,-2,1,-1,2,-2,1,-1};
    class NetFlow
    {
    	private:
    		#define PS (N*N+2)
    		#define ES (5*N*N)
    		#define s (n*n+1)
    		#define t (n*n+2)
    		#define P(i,j) (((i)-1)*n+(j))
    		#define add(x,y,f) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f)
    		int ee,lnk[PS+5],cur[PS+5],d[PS+5],q[PS+5];struct edge {int F,to,nxt;}e[2*ES+5];
    		I bool BFS()
    		{
    			RI i,k,H=1,T=1;for(i=1;i<=t;++i) d[i]=0;d[q[1]=s]=1;W(H<=T&&!d[t])
    				for(i=lnk[k=q[H++]];i;i=e[i].nxt) e[i].F&&!d[e[i].to]&&(d[q[++T]=e[i].to]=d[k]+1);
    			return d[t]&&memcpy(cur,lnk,sizeof(lnk)),d[t];
    		}
    		I int DFS(CI x=s,RI f=1e9)
    		{
    			if(!f||x==t) return f;RI i,g,res=0;for(i=cur[x];i;i=e[i].nxt)
    			{
    				if(d[e[i].to]^(d[x]+1)||!(g=DFS(e[i].to,min(f,e[i].F)))) continue;
    				if(e[i].F-=g,e[((i-1)^1)+1].F+=g,res+=g,!(f-=g)) break;
    			}return cur[x]=i,res;
    		}
    	public:
    		I void Add(CI x,CI y,CI f) {add(x,y,f),add(y,x,0);}
    		I int MaxFlow() {RI f=0;W(BFS()) f+=DFS();return f;}
    }D;
    int main()
    {
    	RI i,j,k,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),p[x][y]=1;
    	for(i=1;i<=n;++i) for(j=1;j<=n;++j) if(!p[i][j])//有障碍的点不参与连边
    	{
    		#define Check(x,y) (1<=(x)&&(x)<=n&&1<=(y)&&(y)<=n&&!p[x][y])//对应格子是否存在且无障碍
    		if((i^j)&1) for(D.Add(s,P(i,j),1),k=0;k^8;++k)//超级源连向黑点
    			Check(i+dx[k],j+dy[k])&&(D.Add(P(i,j),P(i+dx[k],j+dy[k]),INF),0);//黑点连向能攻击的白点
    		else D.Add(P(i,j),t,1);//白点连向超级汇
    	}
    	return printf("%lld
    ",n*n-m-D.MaxFlow()),0;//总点数减规定障碍数减最大流
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    android系统webview使用input实现选择文件并预览
    在列表中动态设置元素的id
    Vue使用Clipboard.JS在h5页面中复制内容
    Vue使用v-for显示列表时,数组里的item数据更新,视图中列表不同步更新的解决方法
    Vue子组件和根组件的关系
    Vue生命周期和钩子函数及使用keeplive缓存页面不重新加载
    Python与数据结构[3] -> 树/Tree[0] -> 二叉树及遍历二叉树的 Python 实现
    Python与数据结构[2] -> 队列/Queue[0] -> 数组队列的 Python 实现
    Python与数据结构[1] -> 栈/Stack[1] -> 中缀表达式与后缀表达式的转换和计算
    Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3355.html
Copyright © 2020-2023  润新知