题目描述
两个PACMAN吃豆豆。一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方。PACMAN走到豆豆处就会吃掉它。PACMAN行走的路线很奇怪,只能向右走或者向上走,他们行走的路线不可以相交。 请你帮这两个PACMAN计算一下,他们俩加起来最多能吃掉多少豆豆。
输入格式
第一行为一个整数N,表示豆豆的数目。 接下来 N 行,每行一对正整数,表示第i个豆豆的坐标。任意两个豆豆的坐标都不会重合。
输出格式
仅有一行包含一个整数,即两个PACMAN加起来最多能吃掉的豆豆数量
输入输出样例
输入 #1 复制
8
8 1
1 5
5 7
2 2
7 8
4 6
3 3
6 4
输出 #1 复制
7
说明/提示
N < = 2000
纪念自己洛谷第一道黑题。。。。。
看到这道题,我们会想到跑费用流,但是由于点数太多,我们不能一一去连边,这样肯定会TLE,那我们就需要做一些剪枝了,对于每个点先按照x坐标排序,然后对于每个点我们肯定只能连到它右上的点。然后假设我们现在已经连了右上的第一个点,那我们怎么做呢?对于连了这个点的后面的比我们连的这个点y坐标大的话,那么这个点肯定会被我们第一个连的点所相连,这样这个点就是不需要相连的,然后跑一次费用流即可。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e4+10,M=1e6+10;
int n,S,T,v[N],e[N],d[N];
int head[N],nex[M],w[M],to[M],flow[M],tot=1;
struct node{
int x,y;
}t[N];
int cmp(const node a,const node b){
if(a.x!=b.x) return a.x<b.x; return a.y<b.y;
}
inline void ade(int a,int b,int c,int d){
to[++tot]=b; w[tot]=d; flow[tot]=c; nex[tot]=head[a]; head[a]=tot;
}
inline void add(int a,int b,int c,int d){
ade(a,b,c,d); ade(b,a,0,-d);
}
int spfa(){
memset(d,0xcf,sizeof d); queue<int> q; q.push(S);
d[S]=0; int vis[N]={0}; vis[S]=1;
while(q.size()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=nex[i]){
if(flow[i]>0&&d[to[i]]<d[u]+w[i]){
d[to[i]]=d[u]+w[i];
v[to[i]]=u; e[to[i]]=i;
if(!vis[to[i]]) q.push(to[i]),vis[to[i]]=1;
}
}
}
return d[T]!=0xcfcfcfcf;
}
int EK(){
int res=0;
while(spfa()){
int mi=0x3f3f3f3f;
for(int i=T;i!=S;i=v[i]) mi=min(mi,flow[e[i]]);
for(int i=T;i!=S;i=v[i]) flow[e[i]]-=mi,flow[e[i]^1]+=mi;
res+=mi*d[T];
}
return res;
}
signed main(){
cin>>n; S=4*n+1; T=S+1; int m=n+1;
for(int i=1;i<=n;i++) cin>>t[i].x>>t[i].y;
t[0].x=t[0].y=0;
sort(t+1,t+1+n,cmp); add(S,0,2,0);
for(int i=0;i<=n;i++){
int mi=0;
for(int j=i+1;j<=n;j++){
if(t[i].y>t[j].y) continue;
if((!mi)||t[j].y<mi){
add(i+m,j,2,0); mi=t[j].y;
}
}
}
add(0,m,2,0);
for(int i=1;i<=n;i++) add(i,i+m,1,1),add(i,i+m,1,0),add(i+m,T,2,0);
cout<<EK()<<endl;
return 0;
}