图的最大团:”任意两点之间都有一条边相连“的子图被称为无向图的团,点数最多的团为图的最大团

朋友圈中任意两个点之间都有关系,既是图中的团。
答案就是图中的最大团。

我们如果把B国的人分成奇数和偶数两类,就会发现奇数和偶数这两部分都是一个团
而且这两部分之间有一些连边
很像二分图是吧,就只是左右两边的点从两两没边变成了两两有边
于是我们取一个补图,这张图就变成了一张二分图
补图有一个非常好的性质,补图最大独立集等于原图最大团
这个很好理解吗,最大团要求两两有边,最大独立集要求两两没边,于是把边的存在性取反之后两者是等价的而二分图的最大独立集等于总点数-最小点覆盖
最小点覆盖等于最大匹配
于是B国的情况我们就解决了
再来看看A国和跨国关系
A国就是一个n2n^2n2的二分图,因此最多只选两个人就可以把整个国家给覆盖掉了。
因此A国中只能选择0,1,2人这三种情况,于是我们枚举在A国里选择哪些人,之后处理出B国中的和这些人都有朋友关系的人,对这些点跑最大独立集就好了
【优化】
直接跑Hungary是过不去的。问题在哪里呢?因为一直memset太挫了!
这个时候我们需要用到时钟T1、T2和tim、vis两个数组。还有思路①处的ban数组也注意一下。
到底什么是时间戳呢?好像并没有人解释。弄了一天大致弄出了一个比较清晰的解释:
一般来说匈牙利算法是这样弄得。每次匈牙利算法前将lk数组清空为-1(整个过程中枚举了A中0个1个2个3个人),然后匈牙利算法内部的循环中将vis数组清零。还是这句话——太挫了!!!!
T1在每次枚举开始+1。它用在lk数组上。由于lk数组并没有清空,之前可能已经lk过了,但是事实上每次匈牙利算法,lk是要清空的。所以如果tim[x]的值!=T1,就表示当前这次枚举中这个点还没有连接过,相当于lk[x]=0;如果tim[x]=T1了,说明当前这次已经用过它了,也就是之前清零过了的含义,那么按照朴素的匈牙利来做。
T2则在匈牙利算法中的循环语句中使用,出现在find前,这个是减去vis数组优化的,道理和上面差不多。vis[x]=T2表示当前这次find已经访问过了,而vis[x]!=T2就表示当前没有访问过,相当于vis=0或1。
另外,ban也是一个道理,ban[x]=T1表示当前这次x被ban掉了。

综上所述,时间戳并不是像是一些地方所说的一样用来在上一次匈牙利算法的基础上进行增广,而是真的、纯粹地免去memset的过程而已……
再简单点概述,如果有i次操作,每次vis数组都要清空的话,那么第i次操作不如这样转换:!vis[x]→vis[x]!=i,vis[x]→vis[x]=i。
所以上面因为lk数组每次枚举只要清零一次,ban数组也只需要ban一次,所以T1只在三种情况的的开头清空;而vis数组在for循环中每次都要清空,所以同理T2也要一直+1。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXA=200+50;
const int MAXB=3000+50;
int na,nb,m;
int a[MAXA],b[MAXB];
bool map[MAXA][MAXB];
vector<int> E[MAXB];
int T1=0,T2=0,ban[MAXB],tim[MAXB],vis[MAXB],lk[MAXB];void addedge(int u,int v)
{E[u].push_back(v);
}int count(int x)
{int re=0;while (x){re+=x&1;x>>=1;}return re;
}int find(int x)
{if (ban[x]==T1)return 0;for (int i=0;i<E[x].size();i++){int to=E[x][i];if ((ban[to]!=T1) && (vis[to]!=T2)){vis[to]=T2;if (tim[to]!=T1 || !lk[to] || find(lk[to])){tim[to]=T1;lk[to]=x;return 1;}}}return 0;
}int mis(int x=0,int y=0)
{++T1;int ret=0;for (int i=1;i<=nb;i++)if (map[x][i] || map[y][i]) ban[i]=T1,++ret;for (int i=1;i<=nb;i++)if (!(b[i]&1)){++T2;if (find(i)) ++ret;}return (nb-ret);
}void init()
{memset(map,1,sizeof(map));scanf("%d%d%d",&na,&nb,&m);for (int i=1;i<=na;i++) scanf("%d",&a[i]);for (int i=1;i<=nb;i++) scanf("%d",&b[i]);for (int i=0;i<m;i++){int x,y;scanf("%d%d",&x,&y);map[x][y]=0;}
}void solve()
{for (int i=1;i<=nb;i++)if (!(b[i]&1))for (int j=1;j<=nb;j++)if (b[j]&1)if (!(count(b[i]|b[j])&1))addedge(i,j);for (int i=1;i<=nb;i++) map[0][i]=0;int ans=mis();for (int i=1;i<=na;i++)ans=max(ans,mis(i)+1);for (int i=1;i<=na;i++)if (a[i]&1)for (int j=1;j<=na;j++)if (!(a[j]&1))ans=max(ans,mis(i,j)+2);printf("%d",ans);
}int main()
{init();solve();return 0;
}

未AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
//对匈牙利算法的一个优化
//为什么不直接跑Dinnc呢
/*就是使用时间戳优化, 去掉了原匈牙利算法中最耗时的memset,降低整个的时间效率*/
using namespace std;const int N = 507, M = 5007;int n, m, na, nb;
int a[N], b[M];
bool map[N][M];
int T1, T2;
int ban[M];//特判数组
int tim[M];//时间戳优化
int match[M];//匈牙利
int vis[M];// bool
int ver[M], nex[M], head[N], tot;void add(int x,int y){ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}int count(int x){//计算有多少个1int res = 0;while(x){res += x & 1;x >>= 1;}return res;
}bool find(int x){if(ban[x] == T1)//每一组不一样,防止出错return false;//已经被ban掉了就拜拜,因为这里是补图,原来满足这个条件的是连通的,补图就变成不连通的了for(int i = head[x];~i;i = nex[i]){int y = ver[i];if(ban[y] != T1 && (vis[y] != T2)){vis[y] = T2;if(tim[y] != T1 || !match[y] || find(match[y])){tim[y] =T1;match[y] = x;return true;}}}return false;
}//匹配
int mis(int x = 0, int y = 0){//不传就是0,传了就覆盖++ T1;//第T1组匹配int res = 0;for(int i = 1;i <= nb;++i)//A国与B国匹配if(map[x][i] || map[y][i])ban[i] = T1, ++res;for(int i = 1;i <= nb;++i){if(!(b[i] & 1)){++ T2;if(find(i))++ res;}}return nb - res;
}void init(){memset(map,1,sizeof map);memset(head,-1,sizeof head);tot = 0;scanf("%d%d%d",&na, &nb, &m);for(int i = 1;i <= na;++i)scanf("%d",&a[i]);for(int i = 1;i <= nb;++i)scanf("%d",&b[i]);for(int i = 1;i <= m;++i){int x,y;scanf("%d%d",&x,&y);map[x][y] = 0;//补图}
}void solve(){//B国所有奇数与奇数,偶数与偶数,奇数与偶数符合条件,三种连边for(int i = 1;i <= nb;++i)if(!(b[i] & 1))//如果是奇数for(int j = 1;j <= nb;++j)if(b[i] & 1)if(!(count(b[i] | b[j]) & 1))//这两个奇数与偶数 | 以后二进制下有奇数个1add(i,j);//补图,原来不能连的给它连起来for(int i = 1;i <= nb;++i)map[0][i] = 0;//补图//A国//first 0 直接B国自己匹配int ans = mis();//second 1for(int i = 1;i <= na;++ i)ans = max(ans,mis(i) + 1);//third 2for(int i = 1;i <= na;++ i)if(a[i] & 1)for(int j = 1;j <= na;++ j)ans = max(ans,mis(i, j) + 2);printf("%d\n",ans);}int t;int main(){scanf("%d",&t);while(t -- ){init();solve();}return 0;
}

题解

解题报告:luogu P2423 [HEOI2012]朋友圈【最大团转最大点独立集(匈牙利算法+时间戳优化)】相关推荐

  1. luogu P2423 [HEOI2012]朋友圈 (最大团)

    在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着. 一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最大数目.两个国家看成是 ...

  2. 【BZOJ 2744 】[HEOI2012]朋友圈

    Description 在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着.一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最 ...

  3. bzoj2744[HEOI2012]朋友圈

    题目链接:bzoj2744 题目大意: 两个国家看成是AB两国,现在是两个国家的描述: 1.A国:每个人都有一个友善值,当两个A国人的友善值a.b,如果a xor b mod 2=1,那么这两个人都是 ...

  4. [HEOI2012] 朋友圈(最大团 + 结论 + 二分图 + 网络流)

    problem luogu-P2423 solution 本题即求无向图最大团问题.这是个 NP hard\text{NP hard}NP hard 问题,所以必须从图的特殊性质出发,否则只能暴搜. ...

  5. 解题报告:X、骑士共存问题(最大独立集)(匈牙利 / 最大流)

    X.骑士共存问题(最大独立集)(匈牙利 / 最大流) 题目链接 [问题分析] 二分图最大独立集,转化为二分图最大匹配,从而用最大流解决. [建模方法] 首先把棋盘黑白染色,使相邻格子颜色不同.把所有可 ...

  6. BZOJ 2744: [HEOI2012]朋友圈

    传送门 解题思路 直接跑最大团洛谷上能得70分,惊了.说说正解,首先A国的必须xor后mod2余1,就相当于两个人必须是1奇1偶,所以A国的人只能选0,1,2个,我们可以暴力枚举选谁.继续考虑B国,现 ...

  7. HEOI2012 朋友圈

    这是做的最得意的一道题,当时在考场上得了90,是因为数据中A有100++的情况,Runtime_error了.. 首先强调,本人不是用二分图,或者说不用图论算法,只涉及简单的枚举和记忆化思想. A国的 ...

  8. cogs 2620. [HEOI2012]朋友圈

    /*70分 被卡T*/ #include<iostream> #include<cstdio> #include<cstring> using namespace ...

  9. BZOJ2744洛谷P2423P2423 [HEOI2012]朋友圈

    自从有了课件,我就不想打字了 代码(交在BZOJ的时候记得把组数删掉) //By AcerMo%%%尹兄 #include<cmath> #include<cstdio> #i ...

最新文章

  1. 高频面试题:Spring 如何解决循环依赖?
  2. python安装软件 No module named setuptools
  3. Nginx的proxy_cache缓存功能
  4. Chapter 1:Introduction
  5. linux Centos7下安装python3及pip3
  6. Node.js 第二个思路
  7. boost::heap模块实现可变堆的测试程序
  8. springboot 不响应字段为空_面试官扎心一问:Tomcat 在 SpringBoot 中是如何启动的?...
  9. 如何更有价值采集数据、高效分析数据?
  10. SAP License:一位在ERP行业从业九年的经验总结
  11. HDU2080 夹角有多大II【数学】
  12. cloudera manager安装使用
  13. 用springMVC拦截器实现操作日志管理功能
  14. opengl导入obj模型
  15. python 输入一个整数,将该整数逆向输出
  16. CTS、CLS和CLR
  17. 前端工程师项目能力精选文章50篇
  18. 记录:Base64编码步骤详解
  19. Unity 游戏实例开发集合 之 FlappyBird (像素鸟) 休闲小游戏快速实现
  20. 你的简历能帮你争取到面试机会吗?

热门文章

  1. Python OpenCV像素操作
  2. 背包思想计算方案的总数(货币系统)
  3. BCH专属“谷歌地图”凸显BCH魅力
  4. 部分 II. Network
  5. Linux网络 - 数据包的发送过程
  6. 容器LinkedList原理(学习)
  7. AngularJs 取消对 html 字符串标签转义
  8. 高频交易都有哪些著名的算法
  9. 一、cocos2dx之如何优化内存使用(高级篇)
  10. Backbone的写类方式