1 条题解
-
0
自动搬运
来自洛谷,原作者为

Windows_XP
**搬运于
2025-08-24 21:27:24,当前版本为作者最后更新于2018-03-12 16:11:41,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题为什么要二分图呢?太大了好像划不住
这题我们bfs一下就好了。二分图又不好写,还慢的要死,
还丑,不如好写稳定的bfs。说是bfs其实就是乱搞。
以下几段通俗易懂
可爱面向萌新。大佬可以直接跳过看总结。一个装备只能提供一个属性。我们把两个属性值当做两个点来连线,那么我们可以感性地想象这条线上有一只猫,这只猫要不然趴在一头,要不然趴在另一头,哪个值被覆盖代表着这个装备提供哪个属性值。如果将属性值来这样建一张图,对于每一个连通图,因为每一只猫都能覆盖旁边的一个点,我们可以很轻松的想象到,如果说这个图中有m条边,n个点,那么一定可以有min(n,m)个点被覆盖。
除了一个树以外,图上的边数一定大于等于点数。显然吧!那么说明图中的所有点都是可以被猫猫搞到的,树中有且只有一个点无法被覆盖。根据贪心的思想我们当然是让那个最大的值的那个点无法被覆盖。我们可以用bfs 通过对遍历到的边数和点数的比较 来维护这个过程,(不知道怎么维护可以看代码我会尽量的!写的非常!详细
毕竟我想过审核)而对于树中最大的点如何处理呢?每一次bfs都会遍历整个连通块,其他的bfs是够不到这个最大点的。所以最大的那个点无法被其他的bfs覆盖。那么我们将它的状态视为cant,即它绝对无法被覆盖。
没有与任何点相连的点,也可以看做一个树。
于是我们可以从1开始,向10000遍历。每一个值都是属性值,将每一个值都看做点。如果当前这个点没有被bfs过就bfs它
废话,要不然就判断是否是cant点。总结在这
总结:将属性值连边,每一个强联通分量内的所有点都可选,每一个树内最大点不可选,用bfs判断。根本就不用缩点。(并查集可能会更快?我写不明白..)每一条边与每一个点都至多会被判断一次,上界复杂度O(n+m) (要是2就cant了,那么当然后面全不用判了)。
多简单...
说实话,处理结果的思路与楼下用并查集的大佬们的思路很像,但是又略有不同。严格地说并不是同一种方法。
代码!
#include <bits/stdc++.h> //我就好这一口 #define rap(i,s,n) for(int i=s;i<=n;i++)//同上 #define drap(i,s,n) for(int i=s;i>=n;i--)//同上 #define N 23333 #define M 2333333 using namespace std; char xB[1<<15],*xS=xB,*xTT=xB;//读优 原因同上 #define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++) #define isd(c) ((c>='0'&&c<='9')||(c=='-')) template<typename T> inline void rd(T & xaa){ char xchh; T f=1; xaa=0; while(xchh=getc(),!isd(xchh)); if(xchh=='-'){f=-1; xchh=getc();} xaa=xchh-'0'; while(xchh=getc(),isd(xchh)) xaa=xaa*10+xchh-'0'; xaa*=f; return; } int m,to[M],nxt[M],head[N],cnt;//存图 bool vis[N],cant[N];//bfs用的 void add(int a,int b){cnt++; to[cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; return;} bool bfs(int k){//返回是否能覆盖 //printf("bfs(%d)\n",k); if(!vis[k]){ vis[k]=1; int maxp=k,nump=0,nume=0; queue<int>q; q.push(k); //maxp:涉及到的最大的点。设成0不会影响结果。 //nump:点数 nume:边数 while(!q.empty()){ int u=q.front(); q.pop(); maxp=max(maxp,u); nump++; for(int i=head[u];i;i=nxt[i]){nume++; if(!vis[to[i]]) vis[to[i]]=1,q.push(to[i]);} } //每bfs一个点都让nump++(显然),每碰到一条边都让nume++。 //但是因为是双向图,每条边都碰到了两遍。所以/2 nume>>=1; if(nume<nump) cant[maxp]=1; //rap(i,1,maxp) printf("%d ",vis[i]); printf("\n"); //rap(i,1,maxp) printf("%d ",cant[i]); printf("\n"); } return (!cant[k]);//判断当前点是否不可覆盖(其实只有它自己孤零零不和其他点相连才是0... } int main(){ //freopen("1640.in","r",stdin); rd(m); int a,b; rap(i,1,m){rd(a),rd(b); add(a,b); add(b,a);} int k=1; while((vis[k]&&!cant[k])||bfs(k)) k++; printf("%d\n",k-1); //注意while中的判断。只有vis过才能判断cant值,否则cant值没有意义。没有vis过就会bfs。 return 0; }点个赞吧 ^^(用一个PAFF酱的表情)
- 1
信息
- ID
- 632
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者