写的很菜 , 欢迎建议、补充
二分图的定义
图G=(V,E),顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。-----百科
就是当一个东西可以分成两半的时候,你可以问自己一下这满足不满足二分图的性质。
解释的很草率 ↑ , 题解里向来都是说 : "显而易见这是二分图", 我也不好这么说 , 题做得还少, 这几天多练练没准就找到手感了 。
当你定好了解题的方向,那么如何判定这个是二分图呢 。
染色法 。
需要两种颜色,我们从任意点开始染色, 把与其相邻的点染成与它相反的颜色 , 整个图染完色之后,如果满足相连的两个点颜色都不同, 那么就可以断定它是二分图了 , 如果发现相连点颜色相同 ,那就满足不了二分图的定义了。
这个染色过程通过BFS或者DFS实现。
下面贴个BFS代码 , DFS同理。
1 void connect(int x,int y)//拿邻接表存的图,用vector也可以。 2 { 3 pre[++cnt]=last[x]; 4 other[cnt]=y; 5 last[x]=cnt; 6 } 7 void BFS(int x) 8 { 9 queue <int>q; 10 q.push(x); 11 color[x]=1; //BFS染色 12 while(!q.empty()) 13 { 14 int tmp=q.front(); 15 q.pop(); 16 for(int i=last[tmp];i;i=pre[i]) 17 { 18 int to=other[i]; 19 if(to==tmp)continue; 20 if(color[to]==color[tmp]){ //发现相邻点的颜色相等,于是这个图不是二分图 21 exit(0); 22 } 23 else{ 24 color[to]=1^color[tmp]; //把相邻点涂成相反的颜色,然后把这个点加入队列 25 q.push(to); 26 } 27 } 28 } 29 } 30 int main() 31 { 32 memset(color,-1,sizeof(color)); 33 for(int i=1;i<=N;i++) 34 if(color[i]==-1)BFS(i); //=、= 35 }
判断二分图的代码就这样↑
下面给道可爱的例题
NOIP2008 双栈排序 (可以在LuoguP1155提交)
戳链接https://www.luogu.org/problem/show?pid=1155看题面,就不粘贴了
这个题就是有一个序列,给你两个栈,通过进栈退栈输出一个排序的序列
(按理来说肯定不能纯模拟做,虽然题解里有一个超级长的纯模拟代码)
联想到二分图,我们应该去思考如何把这些数分成两部分,分别放进两个栈里
下面我们要考虑怎样的两个元素可以放到一个栈里面,
显然的 ,当i<j<k , a[k]<a[i]<a[j],i和j是不能放到同一个栈里的 ,于是i,j就可以构成图的两个子集的一条边
我们枚举出来所有的i,j 进行连边,
for(int i=N-1;i>=1;i--)sufmin[i]=min(sufmin[i+1],a[i+1]); for(int i=1;i<=N;i++) for(int j=i+1;j<=N;j++) if(a[i]<a[j]&&a[i]>sufmin[j]){ connect(i,j);connect(j,i); }
之后利用上面的染色法判定二分图,就可以把所有的数分成两部分,再进行简单的模拟,就可以得到答案了。
如果不是这个不是二分图,那就说明无解。
先写这么多了,很啰嗦求见谅,学了新的再加内容。