传送门
/*
Q版泡泡堂玩过吗?
给你n*n的图,x表示墙壁,问最多可以放多少个炸弹?
炸弹可炸此行此列的所有空位
分别按行按列缩点,求二分图最大匹配。
*/
#include
#define lson rt<<1
#define rson rt<<1|1
#define all(x) x.begin(),x.end()
#define iis std::ios::sync_with_stdio(false)
#define mme(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
typedef pair pii;
const int MXN = 1e2+7;
const int MXE = 4e6+1e5+7;
const int INF = 0x3f3f3f3f;
const int MOD = (int)1e9 + 7;
int n, m;
char ar[MXN][MXN];
int row[MXN][MXN], col[MXN][MXN];
int vis[MXN], is[MXN], be[MXN];
std::vector son[MXN];
bool dfs(int u){
for(auto x : son[u]){
if(vis[x]) continue;
vis[x] = 1;
if(is[x] == -1 || dfs(is[x])){
is[x] = u;
be[u] = x;
return true;
}
}
return false;
}
int main(){
while(~scanf("%d", &n)&&n){
for(int i = 0; i <= 16; ++i)son[i].clear();
for(int i = 0; i < n; ++i){
scanf("%s", ar[i]);
}
mme(row, -1);
mme(col, -1);
int a = 0, b = 0;
for(int i = 0, tmp; i < n; ++i){
for(int j = 0; j < n; ++j){
if(ar[i][j] == '.'&&row[i][j] == -1){
row[i][j] = ++a;
tmp = j;
while(tmp < n && ar[i][tmp] == '.'){
row[i][tmp] = a; tmp++;
}
}
if(ar[j][i] == '.'&&col[j][i] == -1){
col[j][i] = ++b;
tmp = j;
while(tmp < n && ar[tmp][i] == '.'){
col[tmp][i] = b; tmp++;
}
}
}
}
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(ar[i][j] == '.'){
son[row[i][j]].push_back(col[i][j]);
}
}
}
for(int i = 1 ; i <= a; ++i){
sort(son[i].begin(),son[i].end());
son[i].erase(unique(son[i].begin(),son[i].end()),son[i].end());
}
mme(is, -1);
mme(be, -1);
int cnt = 0;
for(int i = 1; i <= a; ++i){
if(be[i] != -1) continue;
mme(vis, 0);
if(dfs(i)){
cnt++;
}
}
printf("%d\n", cnt);
}
return 0;
}
传送门
/*
判断是不是二分图,若是求最大匹配
判断二分图:
1.并查集
2.染色法
*/
#include
#define lson rt<<1
#define rson rt<<1|1
#define all(x) x.begin(),x.end()
#define iis std::ios::sync_with_stdio(false)
#define mme(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
typedef pair pii;
const int MXN = 1e3+7;
const int MXE = 4e6+1e5+7;
const int INF = 0x3f3f3f3f;
const int MOD = (int)1e9 + 7;
int n, m, FLAG;
char ar[MXN][MXN];
int vis[MXN], is[MXN], be[MXN];
std::vector son[MXN];
int fa[MXN];
bool dfs(int u){
for(auto x : son[u]){
if(vis[x]) continue;
vis[x] = 1;
if(is[x] == -1 || dfs(is[x])){
is[x] = u;
be[u] = x;
return true;
}
}
return false;
}
int Fi(int x){
return fa[x] == x? x: fa[x] = Fi(fa[x]);
}
void un(int a,int b){
int pa = Fi(a), pb = Fi(b);
if(pa != pb){
fa[pb] = pa;
}
}
int main(){
while(~scanf("%d%d", &n, &m)){
for(int i = 0; i <= n; ++i) son[i].clear();
for(int i = 1; i <= n*2; ++i) fa[i] = i;
for(int i = 0, u, v; i < m; ++i){
scanf("%d%d", &u, &v);
son[u].push_back(v);
son[v].push_back(u);
un(u, v + n);un(v, u + n);
}
FLAG = 0;
int flag = 0;
for(int i = 1; i <= n && flag == 0; ++i){
if(Fi(i) == Fi(i+n)) flag = 1;
}
if(flag) {
printf("No\n");
continue;
}
mme(is, -1);
mme(be, -1);
int cnt = 0;
for(int i = 1; i <= n; ++i){
if(be[i] != -1) continue;
mme(vis, 0);
if(dfs(i)){
cnt++;
}
}
printf("%d\n", cnt/2);
}
return 0;
}
传送门
/*
m门课程,n个学生,每个学生代表某些课程。
问是否存在此m个学生,满足每个学生能代表一个不同的课程,每门课程都有学生代表
即问是否存在一个m:m的完备匹配。
*/
#include
#define lson rt<<1
#define rson rt<<1|1
#define all(x) x.begin(),x.end()
#define iis std::ios::sync_with_stdio(false)
#define mme(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
typedef pair pii;
const int MXN = 1e3+7;
const int MXE = 4e6+1e5+7;
const int INF = 0x3f3f3f3f;
const int MOD = (int)1e9 + 7;
int n, m, FLAG;
char ar[MXN][MXN];
int vis[MXN], is[MXN], be[MXN];
std::vector son[MXN];
int fa[MXN];
bool dfs(int u){
for(auto x : son[u]){
if(vis[x]) continue;
vis[x] = 1;
if(is[x] == -1 || dfs(is[x])){
is[x] = u;
be[u] = x;
return true;
}
}
return false;
}
int Fi(int x){
return fa[x] == x? x: fa[x] = Fi(fa[x]);
}
void un(int a,int b){
int pa = Fi(a), pb = Fi(b);
if(pa != pb){
fa[pb] = pa;
}
}
int main(){
int tim;
scanf("%d", &tim);
while(tim--){
scanf("%d%d", &m, &n);
for(int i = 0; i <= max(n,m); ++i) son[i].clear();
for(int i = 0, u, v; i < m; ++i){
scanf("%d", &u);
while(u--){
scanf("%d", &v);
son[i+1].push_back(v);
}
}
mme(is, -1);
mme(be, -1);
int cnt = 0;
for(int i = 1; i <= m; ++i){
if(be[i] != -1) continue;
mme(vis, 0);
if(dfs(i)){
cnt++;
}
}
if(cnt >= m) printf("YES\n");
else printf("NO\n");
}
return 0;
}
传送门
KM算法模板(O(N^3))
一.顶点数较少的为X部,X部每个点顶标为该点关联的最大边的权值,Y部的顶点顶标为0。
二.对于X部中的每个顶点,在相等子图中用匈牙利算法找增广路径,如果没有找到,则修改顶标,扩大相等子图,继续找增广路径。当每个点都找到增广路径时,意味每个点都在匹配中,即找到了二分图的完备匹配。该完备匹配即为二分图的最佳匹配。
什么是相等子图呢?
边权等于两端点的顶标之和的边,它们组成的图称为相等子图。
修改顶标:
目的:扩大相等子图。
若未找到增广路径,一定找到了许多条从Xi出发并结束于X部的匹配边与未匹配边交替出现的路径,姑且称之为交错树。
交错树中X部的顶点顶标减去一个值d,交错树中Y部的顶点顶标加上一个值d。
若它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图:
1.两端都在交错树中的边(i,j),其顶标和没有变化;
2.两端都不在交错树中的边(i,j),其顶标也没有变化;
3.X端不在交错树中,Y端在交错树中的边(i,j),其顶标和增大;
它原来不属于相等子图,现在可能进入相等子图:
4.X端在交错树中,Y端不在交错树中的边(i,j),其标和会减小。
三.当X部的所有顶点都找到了增广路径后,则找到了完备匹配,此完备匹配即为最佳匹配。
相等子图的若干性质
在任意时刻,相等子图上的最大权匹配一定小于等于相等子图的顶标和。
在任意时刻,相等子图的顶标和即为所有顶点的顶标和。
扩充相等子图后,相等子图的顶标和将会减小。
当相等子图的最大匹配为原图的完备匹配时,匹配边的权值和等于所有顶点的顶标和,此匹配即为最佳匹配。
bool dfs(int s){
visx[s]=1;
for(int i=1;i<=cnty;i++)
if(!visy[i]){
int t=wx[s]+wy[i]-dis[s][i];
if(t==0) {
visy[i]=1;
if(linky[i]==0||dfs(linky[i])){
linkx[s]=i,linky[i]=s;
return true;
}
}else if(t>0){//找出边权与顶标和的最小的差值
if(t
#include
using namespace std;
const int maxn = 300 + 20;
const int inf = 0x3f3f3f3f;
int lx[maxn],ly[maxn];//结点顶标
int pre[maxn];//结点前驱
int slack[maxn];//结点 顶标与边权 最小差值
bool visx[maxn],visy[maxn];
int map[maxn][maxn];
int num;//村民 房子数量
//寻找可行边
bool dfs(int i){
visx[i] = true;
for(int j = 1;j <= num;j++){
if(visy[j])continue;
int t = lx[i] + ly[j] - map[i][j];
if(t==0){//可行边
slack[j] = 0;
visy[j] = true;//放在下面应该也可以
if(pre[j]==0 || dfs(pre[j])){
pre[j] = i;
return true;
}
}else slack[j] = min(slack[j],t);
}
return false;
}
int KM(){
memset(pre, 0, sizeof(pre));
memset(ly, 0, sizeof(ly));
memset(lx, 0, sizeof(lx));
for(int i = 1;i <= num;i++)
for(int j = 1;j <= num;j++)
if(map[i][j] > lx[i]) lx[i] = map[i][j];
//对每一个x 结点求增广路径增加可行边
for(int x = 1;x <= num;x++){
for(int i = 1;i <= num;i++) slack[i] = inf;
while(true){
memset(visx, false, sizeof(visx));
memset(visy, false, sizeof(visy));
if(dfs(x)) break;
//否则 求出常数 d ,顶标减d
int d = inf;
for(int i = 1;i <= num;i++)
if(!visy[i] && d > slack[i]) d = slack[i];
for(int i = 1;i <= num;i++)
if(visx[i]) lx[i] -= d;
for(int i = 1;i <= num;i++)
//如果i不在增广路径中 由于 visx[i] 已经减去 d 还要更新 slack值
if(visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
int ans = 0;
for(int i = 1;i <= num;i++)
if(pre[i] != 0) ans += map[pre[i]][i];
return ans;
}
void solve(){
cin>>num;
for(int i = 1;i <= num;i++)
for(int j = 1;j <= num;j++)
cin>>map[i][j];
cout<lx[i]) {
lx[i]=weight[i][j];
}
}
}
for(int i=0; i=0) {
sum+=weight[match[i]][i];
}
}
if(!x) sum=-sum;
return sum;
}
void solve(){
for(int i=0;i