P3033 Cow Steeplechase G

· · 题解

题意

## 思路 我们注意到一共有两种线段:横或竖,而且相同种类线段之间都没有交点。为了使最后所有的线段互不相交,只要两条线段有交点,我们就必须从中删去**至少**一条线段。 那么我们可以进行**二分图最大匹配**,令横和竖分别为左右部点,如果两条线段之间有交点,就令对应的点连边。用匈牙利算法求出最大匹配对数 $cnt$,那么最多可以选 $(n-cnt)$ 条线段。 这样做的正确性是显而易见的。我们用**反证法**证明: 假设删完线段后还有交点,那么在二分图中一定有两个点之间**有边**(因为有交点)而且**都未完成匹配**(否则就被删了),那么此时匈牙利算法还没有结束,与删完线段相矛盾。所以如果删完了线段,一定是没有交点的。 那么为什么我们要求**最多**选几条线段,即**最少**删去几条线段,却用二分图**最大**匹配呢?因为如果两点之间有边,它们所代表的两条线段一定要**至少**删去一条线段。我们用二分图最大匹配求出最大匹配对数 $cnt$,即有 $cnt$ 对线段**不能**同时存在,从中至少删去一条。那么我们从每一对中删去**一**条,既保证了答案没有交点,又让删线段数**最小**,选线段数**最大**,因此最后的答案 $(n-cnt)$ 就是能够选的**最大条数**。 ### AC code: ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define ll long long using namespace std; const int N=1e5+5; int n,n1,tot,nd[N],cnt; int x1[N],x2[N],y1[N],y2[N],bk[N],a[N],cp[N]; struct edge{ int v,nxt; }ed[N]; void add(int u,int v){ ed[++tot]={v,nd[u]}; nd[u]=tot; } int hung(int u){//匈牙利算法 bk[u]=1; for(int i=nd[u];i;i=ed[i].nxt){ int v=ed[i].v; if(bk[cp[v]])continue; if(cp[v]==0||hung(cp[v]))return cp[v]=u;//相当于return true } return 0; } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>x1[i]>>y1[i]>>x2[i]>>y2[i]; if(x1[i]>x2[i])swap(x1[i],x2[i]);//不一定x1<=x2,y1<=y2 if(y1[i]>y2[i])swap(y1[i],y2[i]); if(x1[i]==x2[i])a[i]=1,n1++;//竖 else a[i]=2;//横 } for(int i=1;i<n;i++)//有交点就建边 for(int j=i+1;j<=n;j++){ if(a[i]==1&&a[j]==2) if(x1[i]>=x1[j]&&x2[i]<=x2[j]&&y1[j]>=y1[i]&&y2[j]<=y2[i]) add(i,j+n1); if(a[i]==2&&a[j]==1) if(x1[j]>=x1[i]&&x2[j]<=x2[i]&&y1[i]>=y1[j]&&y2[i]<=y2[j]) add(j,i+n1); } for(int i=1;i<=n;i++){ if(a[i]==2)continue; memset(bk,0,sizeof(bk)); if(hung(i))cnt++; } cout<<n-cnt<<endl; return 0; } ``` ## 后记 这其实是二分图求**最大独立集**类型的典型题目,标准做法即求出**最小点覆盖**(最大匹配对数),答案即**总点数** $-$ **最小点覆盖**。 ### The End.