2012年佛山市GDOI选拔赛题 加边

Description


给出一个大小为N × M (1 ≤ N, M ≤ 300)的矩形,矩形的每个位置有一个高度 h (1 ≤ h ≤ 1,000,000)。在相邻的格点间,你可以上下或者左右移动。但是你只能从高的格点走向不比它高的格点。相同高度的格点间两个方向都能移动。
现在,你需要计算出至少需要添加多少条双向边才能使到矩阵任意两点之间都可以互相到达

Solution


一眼题。枚举连边然后强联通分量一下找入度为0与出度为0中比较大的那个就行了

考试的时候脑抽了硬是没写出来
我还是太弱了

Code


#include <stdio.h>
#include <stack>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define min(x, y) (x)<(y)?(x):(y)
#define max(x, y) (x)>(y)?(x):(y)
#define N 200001
#define E N * 21 + 1
#define L 501
struct edge{int x, y, next;}e[E];
int ls[N];
inline void addEdge(int &cnt, int x, int y){
    cnt += 1; e[cnt] = (edge){x, y, ls[x]}; ls[x] = cnt;
}
using std:: stack;
stack<int> st;
int inStack[N], comp[N], dfn[N], low[N], cnt;
inline int dfs(int now){
    dfn[now] = low[now] = ++ cnt;
    inStack[now] = 1;
    st.push(now);
    erg(i, now){
        if (!dfn[e[i].y]){
            dfs(e[i].y);
            low[now] = min(low[now], low[e[i].y]);
        }else if (dfn[e[i].y] && inStack[e[i].y]){
            low[now] = min(low[now], dfn[e[i].y]);
        }
    }
    if (dfn[now] == low[now]){
        comp[0] ++;
        for (int tmp = 0; tmp ^ now; ){
            tmp = st.top(); st.pop();
            comp[tmp] = comp[0];
            inStack[tmp] = 0;
        }
    }
}
inline void tarjan(int n){
    cnt = 0;
    rep(i, 1, n){
        if (!dfn[i]){
            dfs(i);
        }
    }
}
int rc[L][L], idx[L][L], ind[N], oud[N];
int main(void){
    int n, m;
    scanf("%d%d", &n, &m);
    rep(i, 1, n){
        rep(j, 1, m){
            scanf("%d", &rc[i][j]);
            idx[i][j] = ++ idx[0][0];
        }
    }
    int edgeCnt = 0;
    rep(i, 1, n){
        rep(j, 1, m){
            if (i > 1 && rc[i][j] >= rc[i - 1][j]){
                addEdge(edgeCnt, idx[i][j], idx[i - 1][j]);
            }
            if (j > 1 && rc[i][j] >= rc[i][j - 1]){
                addEdge(edgeCnt, idx[i][j], idx[i][j - 1]);
            }
            if (i < n && rc[i][j] >= rc[i + 1][j]){
                addEdge(edgeCnt, idx[i][j], idx[i + 1][j]);
            }
            if (j < m && rc[i][j] >= rc[i][j + 1]){
                addEdge(edgeCnt, idx[i][j], idx[i][j + 1]);
            }
        }
    }
    tarjan(n * m);
    rep(i, 1, edgeCnt){
        if (comp[e[i].x] ^ comp[e[i].y]){
            ind[comp[e[i].y]] ++;
            oud[comp[e[i].x]] ++;
        }
    }
    int indCnt = 0, oudCnt = 0;
    rep(i, 1, comp[0]){
        if (!ind[i]){
            indCnt ++;
        }
        if (!oud[i]){
            oudCnt ++;
        }
    }
    int ans = max(indCnt, oudCnt);
    if (comp[0] == 1){
        ans = 0;
    }
    printf("%d\n", ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值