board (双联通分量)

board

10.5 from idy

观察性质 + 双联通分量
先观察到1个性质:只有当陆地数小于等于2时,才无法完成任务。
然后有1个性质:如果陆地数大于等于3,最多只需要删除2个位置,就可以让图变得不连通。(考虑坐标字典序最小的陆地,它的下边和左边一定没有陆地,删掉它右边和上边的它就不连通了,如果只有它和右边及上边,删掉它也不连通了)。
故,我们只需要判断是否可以只删除1个位置让其不连通,50分是枚举1个位置,然后跑flood。
100分是判断图是不是1个双联通分量(Tarjan)。

来自berrykanry的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;

struct edge{
    int last,v,u;
    bool flag;
}ed[400010];

int n,m,T,flag=0,idc=1,cnt=0,idx=0,tot=0,root,mp[330][330],du[330][330];
int head[200010],vis[200010],ins[200010],dfn[200010],low[200010];
char s[100010];

void add(int u,int v){
    ed[++idc].v=v;
    ed[idc].u=u;
    ed[idc].flag=0;
    ed[idc].last=head[u];
    head[u]=idc;
}

void init()
{
    memset(du,0,sizeof(du));
    memset(mp,0,sizeof(mp));
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    flag=idx=tot=0;
    idc=1;
}

void build(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(mp[i][j]){
                int S=(i-1)*m+j,A=i*m+j,B=(i-1)*m+j+1;
                if(mp[i+1][j]) 
                    add(S,A),add(A,S);
                if(mp[i][j+1]) 
                    add(S,B),add(B,S);
            }
}

void tarjan(int u){
    dfn[u]=low[u]=++idx;
    for(int i=head[u];i;i=ed[i].last){
        int v=ed[i].v;
        if(ed[i].flag) continue ;   
        ed[i].flag=ed[i^1].flag=1;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u] && u!=root) 
                cnt++,flag=1; 
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
}

int main(){
    freopen("board.in","r",stdin);
    freopen("board.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
                if(s[j]=='#'){
                    mp[i][j]=1,tot++;
                    du[i-1][j]++,du[i+1][j]++,du[i][j-1]++,du[i][j+1]++;
                }
        }
        if(tot<=2){
            printf("-1\n");
            continue ;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(mp[i][j] && du[i][j]==1) flag=1;
        if(flag==0){
            build();
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    if(mp[i][j] && !dfn[(i-1)*m+j] && !flag){
                        root=(i-1)*m+j;
                        tarjan((i-1)*m+j);
                    }
            if(cnt) flag=1;
        }
        if(flag) printf("1\n");
        else printf("2\n");
    }
    return 0;
}

笔者的“歪门邪道”
判断那些点是可以连出去的,对于每个点判断四周是否可以连出去,很好理解的,具体细节看代码吧。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define LL long long
#define N 310
using namespace std;

int n, m, cnt = 0, flag = 0;
int mp[N][N], du[N][N];
//queue <int>q;
priority_queue<int,vector<int>,greater<int> >q;

int main(){
    freopen ("board.in", "r", stdin);
    freopen ("board.out", "w", stdout);
    int T; scanf("%d", &T);
    while ( T-- ){
        //init();
        cnt = 0; flag = 0;
        memset(du, 0, sizeof(du));
        memset(mp, 0, sizeof(mp));
        scanf("%d%d", &n, &m);
        for(int i=1; i<=n; i++){
            char s[N]; scanf("%s", s+1);
            for(int j=1; j<=m; j++){
                if(s[j] == '.') {
                    /*if(mp[i-1][j] == 2 || mp[i][j-1] == 2 || mp[i+1][j] == 2 || mp[i][j+1] == 2 || i==0 || i==n || j==0 || j==m)
                        mp[i][j] = 2;*/
                    mp[i][j] = 0;
                    if(i==0 || i==n || j==0 || j==m){
                        mp[i][j] = 2;
                        q.push(i*500 + j);
                    }
                }
                else mp[i][j] = 1, cnt++;
            }
        }
        while(!q.empty()){
            int cc = q.top(); q.pop();
            int y = cc % 500, x = cc / 500;
            if(x<n && mp[x+1][y] == 0) mp[x+1][y] = 2, q.push((x+1)*500 + y);
            if(y<m && mp[x][y+1] == 0) mp[x][y+1] = 2, q.push((x)*500 + y+1);
            if(x>0 && mp[x-1][y] == 0) mp[x-1][y] = 2, q.push((x-1)*500 + y);
            if(y>0 && mp[x][y-1] == 0) mp[x][y-1] = 2, q.push((x)*500 + y-1);
            if(x<n && y<m && mp[x+1][y+1] == 0) mp[x+1][y+1] = 2, q.push((x+1)*500 + y+1);
            if(y<m && x>0 && mp[x-1][y+1] == 0) mp[x-1][y+1] = 2, q.push((x-1)*500 + y+1);
            if(y>0 && x<n && mp[x+1][y-1] == 0) mp[x+1][y-1] = 2, q.push((x+1)*500 + y-1);
            if(x>0 && y>0 && mp[x-1][y-1] == 0) mp[x-1][y-1] = 2, q.push((x-1)*500 + y-1);
        }
        if(cnt <= 2) {  printf("-1\n"); continue;}
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(mp[i][j] == 1){
                    if(j<m && mp[i][j+1] == 1) du[i][j]++, du[i][j+1]++;
                    if(i<n && mp[i+1][j] == 1) du[i][j]++, du[i+1][j]++;
                }
                if(du[i][j] == 1) { printf("1\n"), flag = 1; break;}
                if(du[i][j] == 2) {
                    if(mp[i-1][j] == 1 && mp[i][j-1] == 1 && mp[i-1][j-1] == 2) {   printf("1\n"), flag = 1; break;}
                    if(mp[i-1][j] == 1 && mp[i][j+1] == 1 && mp[i-1][j+1] == 2) {   printf("1\n"), flag = 1; break;}
                    if(mp[i-1][j] == 1 && mp[i+1][j] == 1 && mp[i][j-1] == 2 && mp[i][j+1] == 2) {  printf("1\n"), flag = 1; break;}
                    if(mp[i+1][j] == 1 && mp[i][j-1] == 1 && mp[i+1][j-1] == 2) {   printf("1\n"), flag = 1; break;}
                    if(mp[i+1][j] == 1 && mp[i][j+1] == 1 && mp[i+1][j+1] == 2) {   printf("1\n"), flag = 1; break;}
                    if(mp[i][j-1] == 1 && mp[i][j+1] == 1 && mp[i-1][j] == 2 && mp[i+1][j] == 2) {  printf("1\n"), flag = 1; break;}
                }
                if(du[i][j] == 3){
                    int cc = 0;
                    if(mp[i-1][j] != 1) {
                         if(mp[i+1][j-1] == 2) cc++;
                         if(mp[i+1][j+1] == 2) cc++;
                         if(mp[i-1][j] == 2) cc++;
                         if(cc >= 2) {  printf("1\n"), flag = 1; break;}
                    }
                    if(mp[i+1][j] != 1) {
                         if(mp[i-1][j-1] == 2) cc++;
                         if(mp[i-1][j+1] == 2) cc++;
                         if(mp[i+1][j] == 2) cc++;
                         if(cc >= 2) {  printf("1\n"), flag = 1; break;}
                    }
                    if(mp[i][j-1] != 1) {
                         if(mp[i+1][j+1] == 2) cc++;
                         if(mp[i-1][j+1] == 2) cc++;
                         if(mp[i][j] == 2) cc++;
                         if(cc >= 2) {  printf("1\n"), flag = 1; break;}
                    }
                    if(mp[i][j+1] != 1) {
                         if(mp[i+1][j-1] == 2) cc++;
                         if(mp[i-1][j-1] == 2) cc++;
                         if(mp[i][j+1] == 2) cc++;
                         if(cc >= 2) {  printf("1\n"), flag = 1; break;}
                    }
                }
                if(du[i][j] == 4){
                    int cc = 0;
                     if(mp[i+1][j-1] == 2) cc++;
                     if(mp[i-1][j-1] == 2) cc++;
                     if(mp[i+1][j+1] == 2) cc++;
                     if(mp[i-1][j+1] == 2) cc++;
                     if(cc >= 2) {  printf("1\n"), flag = 1; break;}
                }
            }
            if(flag == 1) break;
        }
        if(flag != 1) printf("2\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值