[无聊测试赛] T10 所驼门王的宝藏

码农题.思维难度中等,难在调试

建边方法:分别将x和y排序,对每个点,对它旁边所有的x/y连边(如果他需要). 开map记录点的位置,对于每个点,如果他的九宫格之内有其他点,就对他连边

注意排序会使点的顺序混乱,故需要在状态里将点的id保存

连完边跑tarjan缩点.易证如果你能到一个环内的某个点,你就可以到达这个环内的每个点.每个环的权值为他里面的点数量.将各个环转化为一个新图

可以发现新图已经转化为一个DAG.可以选择用dfs/topo来记录答案.这里我选择用dfs.

dfs每个未经过的点并统计经过这个点能得到的宝藏数量,取最大值

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <unordered_map>
#include <cstring>
#include <stack>
using namespace std;
const int MAXN = 1e5+5;
int n,r,c,dp[MAXN],curr,ans;

int pre,dfn[MAXN],low[MAXN],head[MAXN],num[MAXN],in[MAXN];
unordered_map<int, unordered_map<int,int> > mp, inq;
vector<int> adj[MAXN],adj2[MAXN],tmp[MAXN];
stack<int> st;
int x_[8] = {1,0,-1,0,1,1,-1,-1};
int y_[8] = {0,1,0,-1,1,-1,1,-1};//九宫格
bool vis[MAXN];
struct Edge{
  int x,y,type,id;
}edge[MAXN],edge2[MAXN];
inline bool sorted1(Edge a, Edge b){
  return a.x<b.x || (a.x==b.x && a.type==1);
}//x排序
inline bool sorted2(Edge a, Edge b){
  return a.y<b.y || (a.y==b.y && a.type==2);
}//y排序
inline void add_x(){
  sort(edge+1,edge+n+1,sorted1);
  int prev = 0;
  for (int i=1;i<=n;i++){
    if (edge[i].type!=1) continue;//如果他自己不能连
    int f = edge[i].id;
    int nxt = i-1;
    while(nxt>0){
      if (edge[nxt].x!=edge[i].x) break;
      int t = edge[nxt].id;
      adj[f].push_back(t);
      nxt--;
    }//往左连
    nxt = i+1;
    while(nxt<=n){
      int t = edge[nxt].id;
      if (edge[nxt].x!=edge[i].x) break;
      adj[f].push_back(t);
      nxt++;
    }//往右连
  }
}
inline void add_y(){
  sort(edge+1,edge+n+1,sorted2);
  int prev = 0;
  for (int i=1;i<=n;i++){
    if (edge[i].type!=2) continue;
    int f = edge[i].id;
    int nxt = i-1;
    while(nxt>0){//往左
      int t = edge[nxt].id;
      if (edge[nxt].y!=edge[i].y) break;
      adj[f].push_back(t);
      nxt--;
    }
    nxt = i+1;
    while(nxt<=n){//往右
      int t = edge[nxt].id;
      if (edge[nxt].y!=edge[i].y) break;
      adj[f].push_back(t);
      nxt++;
    }
  }
}
inline void add_xy(){//九宫格连
  for (int i=1;i<=n;i++){
    if (edge[i].type!=3) continue;
    int f = edge[i].id;
    for (int j=0;j<8;j++){
      int to_x = edge[i].x+x_[j], to_y = edge[i].y+y_[j];
      if (mp[to_x][to_y] ){
        int t = mp[to_x][to_y];
        if (!inq[f][t]){inq[f][t] = true;adj[f].push_back(t);}
      }
    }
  }
}
void tarjan(int pos){//裸的tarjan
  st.push(pos);
  vis[pos] = true;
  dfn[pos] = low[pos] = ++pre;
  for (int v : adj[pos]){
    if (!dfn[v]){
      tarjan(v);
      low[pos] = min(low[pos],low[v]);
    }else if (vis[v]) low[pos] = min(low[pos],dfn[v]);
  }
  if (dfn[pos]==low[pos]) {
    curr++;
    while(st.size()){
      int stt = st.top();
      head[stt] = curr;
      tmp[curr].push_back(stt);
      num[curr]++;
      st.pop();
      vis[stt] = false;
      if (stt==pos) break;
    }
  }
}
void dfs(int pos){//dfs找子叶权值
  dp[pos] = num[pos];
  int addi = 0;
  for (int v : adj2[pos]){
    if (!dp[v]) dfs(v);
    addi = max(addi,dp[v]);
  }
  dp[pos]+=addi;
}
int main(){
  cin >> n >> r >> c;
  for (int i=1;i<=n;i++) {
    cin >> edge[i].x  >> edge[i].y >> edge[i].type;edge[i].id = i;
    edge2[i] = edge[i];
    mp[edge[i].x][edge[i].y] = i;
  }
  add_x();
  add_y();
  add_xy();
  for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
  
  for (int i=1;i<=curr;i++){//建新图
    memset(vis,0,sizeof(vis));
    vis[i] = true;
    for (int u : tmp[i]){
      for (int v : adj[u]){
        if (!vis[head[v]]){
          vis[head[v]] = true;
          adj2[i].push_back(head[v]);
        }
      }
    }
  }
  memset(vis,0,sizeof(vis));
  for (int i=1;i<=curr;i++){//找答案
    if (!vis[i]){
      dfs(i);
      ans = max(ans,dp[i]);
    }
  }
  cout << ans;
}
原文地址:https://www.cnblogs.com/DannyXu/p/12536357.html