Luogu3877 TJOI2010 打扫房间 二分图、网络流

传送门


真是菜死了模板题都不会……

首先\(30 \times 30\)并不能插头DP,但是范围仍然很小所以考虑网络流。

注意每个点都要包含在一个回路中,那么每一个点的度数都必须为\(2\),也就是说每一个点必须向与它四连通的点中恰好\(2\)个连边。而“四连通”又是经典的黑白染色+二分图模型。

所以对于原图黑白染色,原点向黑点、白点向汇点连流量为\(2\)的边,相邻的黑点向白点连流量为\(1\)的边,跑一边Dinic看最大流等于需要打扫的格点数量。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 1e5 + 7 , MAXM = 1e6 + 7;
struct Edge{
    int end , upEd , f , c;
}Ed[MAXM];
int head[MAXN];
int N , M , S , T , cntEd = 1;
queue < int > q;

inline void addEd(int a , int b , int c , int d = 0){
    Ed[++cntEd].end = b;
    Ed[cntEd].upEd = head[a];
    Ed[cntEd].f = c;
    Ed[cntEd].c = d;
    head[a] = cntEd;
}

int cur[MAXN] , dep[MAXN];

inline bool bfs(){
    while(!q.empty())
        q.pop();
    q.push(S);
    memset(dep , 0 , sizeof(dep));
    dep[S] = 1;
    while(!q.empty()){
        int t = q.front();
        q.pop();
        for(int i = head[t] ; i ; i = Ed[i].upEd)
            if(Ed[i].f && !dep[Ed[i].end]){
                dep[Ed[i].end] = dep[t] + 1;
                if(Ed[i].end == T){
                    memcpy(cur , head , sizeof(head));
                    return 1;
                }
                q.push(Ed[i].end);
            }
    }
    return 0;
}

inline int dfs(int x , int mF){
    if(x == T)
        return mF;
    int sum = 0;
    for(int &i = cur[x] ; i ; i = Ed[i].upEd)
        if(Ed[i].f && dep[Ed[i].end] == dep[x] + 1){
            int t = dfs(Ed[i].end , min(mF - sum , Ed[i].f));
            if(t){
                Ed[i].f -= t;
                Ed[i ^ 1].f += t;
                sum += t;
                if(sum == mF)
                    break;
            }
        }
    return sum;
}

int Dinic(){
    int ans = 0;
    while(bfs())
        ans += dfs(S , INF);
    return ans;
}

inline char getc(){
    char c = getchar();
    while(c == ' ' || c == '\n' || c == '\r')
        c = getchar();
    return c;
}

#define id(i,j) (((i) - 1) * M + j)
const int dir[4][2] = {0,1,0,-1,1,0,-1,0};
char mmap[32][32];

int main(){
#ifndef ONLINE_JUDGE
    freopen("in" , "r" , stdin);
    //freopen("out" , "w" , stdout);
#endif
    for(int t = read() ; t ; --t){
        N = read();
        M = read();
        T = N * M + 1;
        memset(head , 0 , sizeof(head));
        cntEd = 1;
        int cnt = 0;
        for(int i = 1 ; i <= N ; ++i)
            for(int j = 1 ; j <= M ; ++j){
                mmap[i][j] = getc();
                if(mmap[i][j] == '.'){
                    ++cnt;
                    if((i + j) & 1){
                        addEd(S , id(i , j) , 2);
                        addEd(id(i , j) , S , 0);
                    }
                    else{
                        addEd(id(i , j) , T , 2);
                        addEd(T , id(i , j) , 0);
                    }
                }
            }
        for(int i = 1 ; i <= N ; ++i)
            for(int j = 1 + (i & 1) ; j <= M ; j += 2){
                if(mmap[i][j] == '.')
                    for(int k = 0 ; k < 4 ; ++k)
                        if(i + dir[k][0] && i + dir[k][0] <= N)
                            if(j + dir[k][1] && j + dir[k][1] <= M)
                                if(mmap[i + dir[k][0]][j + dir[k][1]] == '.'){
                                    addEd(id(i , j) , id(i + dir[k][0] , j + dir[k][1]) , 1);
                                    addEd(id(i + dir[k][0] , j + dir[k][1]) , id(i , j) , 0);
                                }
            }
        puts(!(cnt & 1) && Dinic() == cnt ? "YES" : "NO");
    }
    return 0;
}

转载于:https://www.cnblogs.com/Itst/p/10419524.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值