F - Grid and Tokens
题目描述
有 n n n 颗棋子欲放到 H ∗ W H*W H∗W 的网格中,每颗棋子可放的范围是一个矩阵,且不能有两颗棋子待在同一行或同一列,求最多可以放多少颗棋子。
数据范围与提示
1 ≤ n , H , W ≤ 100 1\le n,H,W\le 100 1≤n,H,W≤100 。
前言
考试时半个小时内就A完了前五题,以为终于可以像 O n e I n D a r k \rm OneInDark OneInDark 一样尝尝AK的感觉(即使是ABC),结果最后一题硬是没建出来。
这题的做法应该是 网络流24题
里面出现过的,不然不会有这么多人选择先做F再做E。可惜我当初没好好学(也许是为了赶进度),网络流只做了两三道练习题就不管了,懈怠了这么强大的算法。
思路
先分析一下结构:
- 每个棋子要恰好选范围内的一行,再选范围内的一列;
- 每一行只能被选一次;
- 每一列只能被选一次。
显然棋子是夹在匹配关系的中间的,所以把每个棋子拆成两个点 U i U_i Ui 、 V i V_i Vi ,建 H H H 个点 R 1 ∼ H R_{1\sim H} R1∼H 表示行,建 W W W 个点 C 1 ∼ W C_{1\sim W} C1∼W 表示列;
(骠一张官方题解的图)
从原点向每个 R i R_i Ri 连一条边(每条边容量都为1,后面也一样),从每个 C i C_i Ci 向汇点连一条边,每个 U i U_i Ui 向 V i V_i Vi 连一条边, R a i ∼ c i R_{a_i\sim c_i} Rai∼ci 分别向 U i U_i Ui 连一条边, V i V_i Vi 向 C b i ∼ d i C_{b_i\sim d_i} Cbi∼di 连一条边。
跑最大流即可。
代码
#include<cstdio>//JZM YYDS!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#define ll long long
#define MAXN 1005
#define MAXM 100005
#define uns unsigned
#define MOD 998244353ll
#define INF 0x3f3f3f3f
#define lowbit(x) ((x)&(-(x)))
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,m,p,A,B,C,D,E;
int f[MAXM],d[MAXN],cur[MAXN],IN;
struct edge{
int v,id;edge(){}
edge(int V,int I){v=V,id=I;}
};
vector<edge>G[MAXN];
inline void addedge(int u,int v,int w){
G[u].push_back(edge(v,IN)),G[v].push_back(edge(u,IN^1));
f[IN]=w,f[IN^1]=0,IN+=2;
}
queue<int>q;
inline bool bfs(int S,int T){
memset(d,-1,sizeof(d));
while(!q.empty())q.pop();
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front();q.pop();
for(uns i=0;i<G[u].size();i++){
int v=G[u][i].v,a=G[u][i].id;
if(f[a]>0&&d[v]<0)d[v]=d[u]+1,q.push(v);
}
}
return d[T]>=0;
}
inline int dfs(int x,int T,int lim){
int res=lim;
if(x==T)return lim;
for(uns i=cur[x];i<G[x].size()&&res;i++){
cur[x]=i;
int v=G[x][i].v,a=G[x][i].id;
if(f[a]>0&&d[v]==d[x]+1){
int ad=dfs(v,T,min(res,f[a]));
res-=ad,f[a]-=ad,f[a^1]+=ad;
}
}
return lim-res;
}
inline int dinic(int S,int T){
int res=0;
while(bfs(S,T)){
memset(cur,0,sizeof(cur));
while(int ad=dfs(S,T,INF))res+=ad;
}
return res;
}
signed main()
{
n=read(),m=read(),p=read();
A=1,B=A+p,C=B+p,D=C+n,E=D+m+1;
for(int i=1;i<=n;i++)addedge(A,C+i,1);
for(int i=1;i<=m;i++)addedge(D+i,E,1);
for(int i=1;i<=p;i++){
addedge(A+i,B+i,1);
int a=read(),b=read(),c=read(),d=read();
for(int j=a;j<=c;j++)addedge(C+j,A+i,1);
for(int j=b;j<=d;j++)addedge(B+i,D+j,1);
}
printf("%d\n",dinic(A,E));
return 0;
}