金牌导航-二分图匹配

金牌导航-二分图匹配

例题A题解

将行和列相匹配,跑最小割即可。

例题A代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e3 + 10, maxm = 1e6 + 10,INF = 0x3f3f3f3f;

int n, m;

int head[maxn], tot = 1;
struct edge{
    int to, nexte, cap, flow;
    edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){}
}e[maxm];
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);}

int dep[maxn], cur[maxn];
int dfs(int u,int flow,int T){
    // printf("%lld\n",u);
    if(u == T)return flow;
    int rest = 0, tmp = 0;
    for(int i = cur[u];i && flow;i = e[i].nexte){
        cur[u] = i; int v = e[i].to;
        if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){
            tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T);
            if(tmp == 0)dep[v] = INF;
            e[i].flow += tmp; e[i ^ 1].flow -= tmp;
            flow -= tmp;rest += tmp;
            if(!flow)return rest;
        }
    }
    return rest;
}
bool bfs(int S,int T){
    queue<int> que;
    for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;}
    que.push(S);dep[S] = 1; cur[S] = head[S];
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(int i = head[u];i;i = e[i].nexte){
            int v = e[i].to;
            if(e[i].cap - e[i].flow > 0 && dep[v] == INF){
                que.push(v);
                cur[v] = head[v];
                dep[v] = dep[u] + 1;
                if(v == T)return 1;
            }
        }
    }
    return 0;
}
int Dinic(int S,int T){
    int mxflow = 0;
    while(bfs(S,T)){mxflow += dfs(S,INF,T);}
    return mxflow;
}

signed main(){
    n = read(); m = read();
    int S = n * 2 + 1, T = n * 2 + 2;
    for(int i = 1;i <= m;i++){
        int u = read(), v = read();
        addd(u, v + n, INF);
    }
    for(int i = 1;i <= n;i++){addd(S,i,1);addd(i + n,T,1);}
    printf("%d\n",Dinic(S,T));
    return 0;
}

例题B题解

老套路,将整个地图按照坐标和奇偶性黑白染色,然后从白点向黑点连边,特别的,不能选择的点不连边。然后跑二分图最大匹配,最大点独立集就是答案。

例题B代码

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10, maxm = 1e6 + 10,INF = 0x3f3f3f3f;

int n;

int head[maxn], tot = 1;
struct edge{
    int to, nexte, cap, flow;
    edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){}
}e[maxm];
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);}

int dep[maxn], cur[maxn];
int dfs(int u,int flow,int T){
    // printf("%lld\n",u);
    if(u == T)return flow;
    int rest = 0, tmp = 0;
    for(int i = cur[u];i && flow;i = e[i].nexte){
        cur[u] = i; int v = e[i].to;
        if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){
            tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T);
            if(tmp == 0)dep[v] = INF;
            e[i].flow += tmp; e[i ^ 1].flow -= tmp;
            flow -= tmp;rest += tmp;
            if(!flow)return rest;
        }
    }
    return rest;
}
bool bfs(int S,int T){
    queue<int> que;
    for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;}
    que.push(S);dep[S] = 1; cur[S] = head[S];
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(int i = head[u];i;i = e[i].nexte){
            int v = e[i].to;
            if(e[i].cap - e[i].flow > 0 && dep[v] == INF){
                que.push(v);
                cur[v] = head[v];
                dep[v] = dep[u] + 1;
                if(v == T)return 1;
            }
        }
    }
    return 0;
}
int Dinic(int S,int T){
    int mxflow = 0;
    while(bfs(S,T)){mxflow += dfs(S,INF,T);}
    return mxflow;
}

int getid(int x,int y){
    if(x < 1 || y < 1 || x > n || y > n)return -1;
    return x * n + y;
}

int mp[600][600];
const int dx[8] = {-2,-1, 1, 2, 2, 1,-1,-2};
const int dy[8] = {-1,-2,-2,-1, 1, 2, 2, 1};

char ch[600];

signed main(){
    n = read();int cnt = n * n;
    int S = n * n + n + 1, T = n * n + n + 2;
    for(int i = 1;i <= n;i++){
        scanf("%s",ch + 1);
        for(int j = 1;j <= n;j++)
            cnt -= (mp[i][j] = ch[j] - '0');
    }
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            if((((i + j) & 1) == 0) && !mp[i][j]){
                int u = getid(i,j);addd(S,u,1);
                for(int k = 0;k < 8;k++){
                    int nx = i + dx[k], ny = j + dy[k];
                    int v = getid(nx,ny);
                    if(v == -1 || mp[nx][ny])continue;
                    addd(u, v, INF);
                }
            }
            else if(((i + j) & 1) && !mp[i][j])addd(getid(i,j),T,1);
        }
    }
    printf("%d\n",cnt - Dinic(S,T));
    return 0;
}

例题C题解

最小路径覆盖,也是老套路了。

例题C代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
    return x * f;
}

const int maxn = 5e3 + 10, maxm = 1e6 + 10, INF = 0x3f3f3f3f;
int n, m;

int head[maxn], tot = 1;
struct edge{
    int to, nexte, cap, flow;
    edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){}
}e[maxm];
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);}

int dep[maxn], cur[maxn];
int dfs(int u,int flow,int T){
    // printf("%lld\n",u);
    if(u == T)return flow;
    int rest = 0, tmp = 0;
    for(int i = cur[u];i && flow;i = e[i].nexte){
        cur[u] = i; int v = e[i].to;
        if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){
            tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T);
            if(tmp == 0)dep[v] = INF;
            e[i].flow += tmp; e[i ^ 1].flow -= tmp;
            flow -= tmp;rest += tmp;
            if(!flow)return rest;
        }
    }
    return rest;
}
bool bfs(int S,int T){
    queue<int> que;
    for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;}
    que.push(S);dep[S] = 1; cur[S] = head[S];
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(int i = head[u];i;i = e[i].nexte){
            int v = e[i].to;
            if(e[i].cap - e[i].flow > 0 && dep[v] == INF){
                que.push(v);
                cur[v] = head[v];
                dep[v] = dep[u] + 1;
                if(v == T)return 1;
            }
        }
    }
    return 0;
}
int Dinic(int S,int T){
    int mxflow = 0;
    while(bfs(S,T)){mxflow += dfs(S,INF,T);}
    return mxflow;
}
int st[maxn];
int sx[maxn], sy[maxn];
int ex[maxn], ey[maxn];
bool ed[maxn][maxn];
void solve(){
    tot = 1;memset(head,0,sizeof(head));
    n = read();int S = n * 2 + 1, T = n * 2 + 2;
    for(int i = 1;i <= n;i++){
        int h, m;
        scanf("%d:%d%d%d%d%d",&h,&m,&sx[i],&sy[i],&ex[i],&ey[i]);
        st[i] = h * 60 + m;
    }
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            if(st[j] > st[i] + abs(ex[i] - sx[j]) + abs(ey[i] - sy[j])
                             + abs(ex[i] - sx[i]) + abs(ey[i] - sy[i]))
                ed[i][j] = 1;
            else ed[i][j] = 0;
    for(int k = 1;k <= n;k++)
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
                ed[i][j] |= (ed[i][k] & ed[k][j]);
    for(int i = 1;i <= n;i++){
        addd(S,i,1);addd(i + n,T,1);
        for(int j = 1;j <= n;j++)
            if(ed[i][j])addd(i,j + n,INF);
    }
    printf("%d\n",n - Dinic(S,T));
}
signed main(){
    int T = read();
    while(T--){solve();}
    return 0;
}

D题解

将整张图黑白染色,然后二分图匹配。
然后发现策略:如果先手走一个必须匹配点,后手就同样走一个必须匹配点,然后先手就输了。
所以先手一定先走非必需匹配点,这个点相邻的点一定是必须匹配点,否则不满足最大匹配。
然后判断非必需匹配点,具体而言,如果在你生成的一个匹配中没有被选择就一定是,否则需要判断在强制不选他的情况下能不能找到一条增广路,如果能就是非必需点。

D代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=  0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 3e4 + 10;
int n, m;
char ch[110][110];
inline int getid(int x,int y){return x * m + y - m;}
vector<int> edg[maxn];
const int dx[4] = { 1, 0, 0,-1};
const int dy[4] = { 0, 1,-1, 0};

int match[maxn];
bool vis[maxn];
bool dfs(int u){
    if(vis[u])return false;vis[u] = 1;
    for(int v : edg[u]){
        if(!match[v] || dfs(match[v])){
            match[v] = u;match[u] = v;
            return true;
        }
    }
    return false;
}
bool dfs1(int u){
    if(vis[u])return false;
    vis[u] = 1;bool res = 0;
    for(int v : edg[u]){
        if(res)return true;
        if(!match[v])return true;
        else res |= dfs1(match[v]);
    }
    return res;
}
vector<pair<int,int> > ans;
bool g[200][200];

signed main(){
    n = read();m = read();
    for(int i = 1;i <= n;i++)
        scanf("%s",ch[i] + 1);
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            if(((i + j) & 1) && ch[i][j] == '.')
            for(int k = 0;k < 4;k++){
                int nx = i + dx[k], ny = j + dy[k];
                if(nx < 1 || nx > n || ny < 1 || ny > m)continue;
                if(ch[nx][ny] == '#')continue;
                edg[getid(i, j)].push_back(getid(nx,ny));
                edg[getid(nx,ny)].push_back(getid(i, j));
            }
        }
    }
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            if(((i + j) & 1)){
                memset(vis,0,sizeof(vis));
                dfs(getid(i,j));
            }
        }
    }
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            if(ch[i][j] == '#')continue;
            if(!match[getid(i, j)])ans.push_back(make_pair(i, j));
            else{
                memset(vis,0,sizeof(vis));
                vis[getid(i, j)] = 1;
                if(dfs1(match[getid(i, j)]))
                    ans.push_back(make_pair(i, j));
            }
        }
    }
    if(ans.size() == 0){puts("LOSE");return 0;}
    else{
        puts("WIN");
        for(auto i : ans)printf("%d %d\n",i.first,i.second);
    }
    return 0;
}
/*
10 12
.##.....##..  
....##....## 
............
###...###.#.
#..####...##
..#....####.
...##....##. 
#...##..##..
#..#........
..#..#.#..## 
*/

例题E题解

首先先将所有数字还原,然后只差一位的数字可以匹配,答案就是最大点独立集。

例题E代码

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 3e3 + 10, INF = 0x3f3f3f3f;

int n, m;
char ch[21];
int a[maxn], cnt;
vector<int> edg[maxn];

int match[maxn];
bool vis[maxn];
bool dfs(int u){
    if(vis[u])return false;vis[u] = 1;
    for(int v : edg[u]){
        if(!match[v] || dfs(match[v])){
            match[v] = u;match[u] = v;
            return true;
        }
    }
    return false;
}

int popcount(int x){
    int res = 0;
    while(x){res += (x & 1);x >>= 1;}
    return res;
}
bool book[maxn];

signed main(){
while((m = read()) | (n = read())){
    cnt = 0;
    for(int i = 1;i <= n;i++){
        scanf("%s",ch);int pos = -1, tmp = 0;
        for(int j = 0;j < m;j++){
            if(ch[j] == '1'){tmp |= (1 << j);}
            if(ch[j] == '*'){pos = j;}
        }
        a[++cnt] = tmp;
        if(pos != -1)a[++cnt] = tmp | (1 << pos);
    }
    n = cnt; sort(a + 1,a + 1 + n);
    n = unique(a + 1,a + 1 + n) - a - 1;
    for(int i = 1;i <= n;i++){edg[i].clear(),match[i] = 0;book[i] = popcount(a[i]) & 1;}
    for(int i = 1;i <= n;i++){
        if(book[i])
        for(int j = 1;j <= n;j++){
            if(i == j || book[j])continue;
            int x = a[i] ^ a[j];
            if(popcount(x) > 1)continue;
            edg[i].push_back(j);
        }
    }
    cnt = 0;
    for(int i = 1;i <= n;i++){
        memset(vis,0,sizeof(vis));
        if(!match[i]) cnt += dfs(i);
    }
    // printf("n = %d\n", n);
    // for(int i = 1;i <= n;i++){
    //     printf("i = %d, a[i] = %d, match[i]=%d\n",i,a[i],match[i]);
    // }
    printf("%d\n",n - cnt);
}
    return 0;
}

例题F题解

本题求最长反链,然后最长反链等于最小链覆盖(可重点),然后最小链覆盖(可重点)等于传递闭包+最小链覆盖(不可重点),然后最小链覆盖(不可重点)等于最大点独立集,最大点独立集等于总点数减去最大匹配,然后就没了。
但是原题要求给出构造,留坑待补。

例题F代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 128, INF = 0x3f3f3f3f, maxm = 300;
int n, m;
bitset<maxn> edg[maxn];

struct edge{
    int to, nexte, cap, flow;
    edge(int to  =0,int ne = 0,int we = 0,int flow = 0):to(to),nexte(ne),cap(we),flow(flow){}
}e[maxm * maxm * 3];
int head[maxm], tot = 1;
void add(int u,int v,int w){e[++tot] = edge(v,head[u],w);head[u] = tot;}
void addd(int u,int v,int w){add(u, v, w);add(v, u, 0);}

int cur[maxm], dis[maxm];
queue<int> que;
int dfs(int u,int flow,int T){
    if(u == T || !flow)return flow; int res = 0;
    for(int i = cur[u];i;i = e[i].nexte){
        int v = e[i].to;cur[u] = i;
        if(dis[v] == dis[u] + 1 && e[i].cap > e[i].flow){
            int tmp = dfs(v, min(flow,e[i].cap - e[i].flow),T);
            if(tmp == 0){dis[v] = INF;}
            e[i].flow += tmp;e[i ^ 1].flow -= tmp;
            res += tmp;flow -= tmp;
            if(!flow)return res;
        }
    }
    return res;
}
bool bfs(int S,int T){
    for(int i = 1;i <= T;i++)cur[i] = 0, dis[i] = INF;
    while(!que.empty())que.pop();
    que.push(S);cur[S] = head[S];dis[S] = 1;
    while(!que.empty()){
        int u = que.front();que.pop();
        for(int i = head[u];i;i = e[i].nexte){
            int v = e[i].to;
            if(dis[v] == INF && e[i].cap > e[i].flow){
                dis[v] = dis[u] + 1;cur[v] = head[v];
                que.push(v);
            }
        }
    }
    return dis[T] != INF;
}
int Dinic(int S,int T){
    int mxflow = 0;
    while(bfs(S,T))mxflow += dfs(S,INF,T);
    return mxflow;
}

signed main(){
    n = read(); m = read();int u, v;
    for(int i = 1;i <= m;i++){u = read(); v = read(); edg[u].set(v);}
    for(int i = 1;i <= n;i++)
        for(int k = 1;k <= n;k++)
            if(edg[i][k]) edg[i] = edg[i] | edg[k];
    // puts("1111");
    int S = n * 2 + 1, T = n * 2 + 2;
    for(int i = 1;i <= n;i++){
        addd(S,i,1);addd(i + n, T, 1);
        for(int j = 1;j <= n;j++)
            if(edg[i][j])addd(i,j + n,INF);
    }
    int res = Dinic(S,T);
    printf("%d\n",n - res);
    return 0;
}
posted @ 2023-12-08 20:34  Call_me_Eric  阅读(65)  评论(0)    收藏  举报
Live2D