题目链接:点击查看

题目大意:给出一个 n * m 的矩阵,矩阵的每一个格子都有一个颜色,颜色非黑即白,除此之外每个格子还有一个指令,分别为:

  1. ' U ':向上一个单位
  2. ' D ':向下一个单位
  3. ' R ':向右一个单位
  4. ' L ':向左一个单位

每个格子都可以放置机器人,对于每个机器人而言,每一秒都会遵循格子上的指令行走,换句话说,机器人会永不停止的行走,现在问,如何放置机器人,可以使得矩阵中机器人的数量最多,且互相永远不会冲突,即任意时刻每个格子里至多有只能一个机器人,在满足矩阵中机器人数量最多的前提下,如何摆放可以使得在黑色格子上的机器人最多,分别输出这两个答案

题目分析:这个题目两个思路,一种思路比较好想,但是细节较多,码量较大,另一种思路需要一定的思维,实现简单,码量小

先从第一种思路说起,因为机器人会永不停止的行走,就说明矩阵中一定存在着首尾相接的环,显然所有环的长度之和就是ans1了,也可以知道了ans1与颜色无关,对于ans2我们需要多考虑一点东西:

在上面这个情况中,点1和点2已经组成了一个首尾相接的环路,所以ans1显然为 2 ,就不多赘述了,但是加上颜色的限制,如果点1和点2同为白色,但是点3为黑色,那么初始时最优的摆放策略肯定是摆在点3和点2,因为这样既能满足ans1=2,且ans2=1,这种情况该如何考虑呢?对于环上的任意一个节点开始,沿着反向边进行遍历,跑出 dis 距离数组就行了,显然如果在这样一个整体中,如果最终不想冲突的话,dis[ i ] % len 的位置上至多只能有一个机器人,此处的 len 为首位相接的环路长度

想清楚上面 dis[ i ] % len 这个地方后,实现就好了,重新捋一下这个题的过程,首先整个矩阵可以视为一个 n * m 个结点和 n * m 条边组成的有向图,既然是有向图,就可以拓扑找环,根据拓扑排序的性质将整个图处理到只剩下首尾相接的环路,此后对于每个环路而言,找到上面的任意一个结点开始,反向 dfs 跑出 dis 数组,然后判断就好了

细节较多,代码如下:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;const int b[4][2]={0,1,0,-1,1,0,-1,0};int mp[150];int n,m,len,du[N],deep[N];//du:入度 deep:在环上的相对位置 len:环的长度 string maze[N],color[N];vector<int>node[N],out[N];//正向边与反向边vector<int>black[N];//black[i]:以点 i 为根节点的环中所牵连的黑点 bool vis[N],flag[N];//vis:是否在环上 flag:找黑点上机器人时用 inline void addedge(int u,int v)
{node[u].push_back(v);out[v].push_back(u);du[v]++;
}inline int get_id(int x,int y)
{return x*m+y;
}void topo()//拓扑找环
{queue<int>q;for(int i=0;i<n*m;i++)if(!du[i])q.push(i);while(q.size()){int u=q.front();q.pop();for(auto v:node[u])if(--du[v]==0)q.push(v);}
}void dfs(int u,int rt,int dep)
{vis[u]=true;deep[u]=dep;if(color[u/m][u%m]=='0')black[rt].push_back(u);for(auto v:out[u]){if(vis[v]){len=deep[u];continue;}dfs(v,rt,dep+1);}
}inline void init()
{for(int i=0;i<n*m;i++){node[i].clear();out[i].clear();black[i].clear();vis[i]=flag[i]=du[i]=0;}
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);mp['R']=0,mp['L']=1,mp['D']=2,mp['U']=3;int w;cin>>w;while(w--){scanf("%d%d",&n,&m);init();for(int i=0;i<n;i++)cin>>color[i];for(int i=0;i<n;i++)cin>>maze[i];for(int i=0;i<n;i++)for(int j=0;j<m;j++){int k=mp[maze[i][j]];addedge(get_id(i,j),get_id(i+b[k][0],j+b[k][1]));}topo();int ans1=0,ans2=0;for(int i=0;i<n*m;i++)if(du[i]&&!vis[i])//在环上且没遍历过,遍历一遍这个环{dfs(i,i,1);ans1+=len;for(auto v:black[i])if(!flag[deep[v]%len]){flag[deep[v]%len]=true;ans2++;}for(auto v:black[i])flag[deep[v]%len]=false;}printf("%d %d\n",ans1,ans2);}return 0;
}

下面说一下第二种思路,假如两个机器人会冲突的话,也就是说在某个时刻,某个格子中同时出现了两个机器人,那么在接下来的移动过程中,显然这两个机器人的路径将保持一致,我们可以先假设 n * m 个格子中初始时都有一个机器人,先让这些机器人运动 n * m 秒,甚至更多,因为 n * m 的一个矩阵,最大可以形成的一个首尾相接的环路长度就是 n * m ,所以先让所有机器人都运动 n * m 秒后,该重合的机器人都已经重合了,并且不难统计每个位置中有多少个来自黑格的机器人,此时矩阵中仍然有机器人的地方,就说明这个位置是首尾相接的环,那么ans1++,如果这个地方至少有一个来自黑格的机器人,那就说明来到这个位置的机器人初始时可以摆在黑格上,即ans2++

实现的话肯定不是暴力模拟 n * m 秒,可以利用倍增,首先将 n * m 个格子按照 0 ~ n * m - 1 编号,那么 dp[ i ][ j ] 就代表编号为 i 的点走 2^j 步可以到达的地方,这里类比于树上倍增就好,剩下的实现起来就比较简单了

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;const int b[4][2]={0,1,0,-1,1,0,-1,0};int mp[150];string maze[N],color[N];int dp[N][20],n,m,num[N][2];//num[i][0]:black num[i][1]:whiteinline int get_id(int x,int y)
{return x*m+y;
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);mp['R']=0,mp['L']=1,mp['D']=2,mp['U']=3;int w;cin>>w;while(w--){scanf("%d%d",&n,&m);for(int i=0;i<n*m;i++)num[i][0]=num[i][1]=0;int limit=log2(n*m)+1;for(int i=0;i<n;i++)cin>>color[i];for(int i=0;i<n;i++)cin>>maze[i];for(int i=0;i<n;i++)for(int j=0;j<m;j++){int k=mp[maze[i][j]];dp[get_id(i,j)][0]=get_id(i+b[k][0],j+b[k][1]);}for(int j=1;j<=limit;j++)for(int i=0;i<n*m;i++)dp[i][j]=dp[dp[i][j-1]][j-1];for(int i=0;i<n;i++)for(int j=0;j<m;j++)num[dp[get_id(i,j)][limit]][color[i][j]-'0']++;int ans1=0,ans2=0;for(int i=0;i<n*m;i++){ans1+=!!(num[i][1]+num[i][0]);//大佬代码学到的,判断一个数是否非零,tqlans2+=!!num[i][0];}printf("%d %d\n",ans1,ans2);}return 0;
}

CodeForces - 1335F Robots on a Grid(拓扑找环+反向dfs/倍增)相关推荐

  1. Educational Codeforces Round 72 (Rated for Div. 2) D. Coloring Edges dfs树/拓扑找环

    传送门 文章目录 题意: 思路: 题意: 给你一张图,你需要给这个图的边染色,保证如果有环那么这个环内边的颜色不全相同,输出染色方案和用的颜色个数. n,m≤5e3n,m\le5e3n,m≤5e3 思 ...

  2. HDU6736 F.Forest Program(dfs找环)

    F.Forest Program 千摆渡题解 找环可以使用dfs一遍求出. 方法为:vis数组设置为三种状态,0表示未被访问过.1表示正在被访问,即边指向的结点是当前结点在dfs树上的祖先节点.2表示 ...

  3. Robots on a Grid CodeForces - 1335F(拓扑排序+正反建图+判环)

    There is a rectangular grid of size n×m. Each cell of the grid is colored black ('0') or white ('1') ...

  4. Mr. Kitayuta‘s Technology CodeForces - 505D(并查集+拓扑排序或dfs找环) 题解

    题目  Shuseki Kingdom is the world's leading nation for innovation and technology. There are n cities ...

  5. [codeforces 1333A] Little Artem 读懂题+找规律+多举例

    Codeforces Round #632 (Div. 2)   比赛人数12810 [codeforces 1333A]   Little Artem   读懂题+找规律+多举例 总目录详见http ...

  6. Codeforces 103B - Cthulhu(并查集 找环和块)

    传送门:https://codeforces.com/problemset/problem/103/B 题意: 给一个n个结点,m条边的无向图 判断给定图是否满足以下条件: 能被表示为有3个及以上的有 ...

  7. [codeforces 1327E] Count The Blocks 打表找规律+根据规律找公式+优化公式

    Educational Codeforces Round 84 (Rated for Div. 2)   比赛人数13522 [codeforces 1327E]  Count The Blocks  ...

  8. Codeforces Round #628 (Div. 2) F. Ehab‘s Last Theorem dfs树

    传送门 文章目录 题意: 思路: 题意: 给你个nnn个点mmm条边的图,可以选择完成以下两个任务中的一个: (1)(1)(1)找出大小恰好为n\sqrt nn​的一个独立集. (2)(2)(2)找出 ...

  9. 【2019.7.16 NOIP模拟赛 T1】洗牌(shuffle)(找环)

    找环 考虑每次洗牌其实是一次置换的过程,而这样必然就会有循环出现. 因此我们直接通过枚举找出每一个循环,询问时只要找到环上对应的位置就可以了. 貌似比我比赛时被卡成\(30\)分的倍增简单多了? 代码 ...

最新文章

  1. mysql基础什么意思,Mysql一些基本概念
  2. windows下使用lighttpd+php(fastcgi)+mysql
  3. linux c 解析生成json(jansson安装和使用)
  4. Vbox配置仅主机模式
  5. 使用PowerDesigner做数据库设计(二)
  6. 【CodeForces - 255B】Code Parsing(思维,字符串)
  7. LeetCode 738. 单调递增的数字(贪心)
  8. Oracle中的伪列
  9. Spark Mllib里的如何对单个数据集用斯皮尔曼计算相关系数
  10. Destoon增加内容页的浏览历史记录
  11. 服务器接显示器卡顿,外接屏幕会出现卡顿、掉帧等问题怎么解决?
  12. 小字辈(左子右兄加强版)
  13. php str splice,PHP array_splice()函数使用方法
  14. 深入探讨企业服务器虚拟化,部署服务器虚拟化的安全风险
  15. 华为三层交换机 配置
  16. ai人工智能将替代人类_教AI学习人类如何有效计划
  17. JSON Schame的介绍及基本使用
  18. android 搜索 时间,Android日历评测 寻找最适合你的日历应用
  19. IPv6篇之简单为美
  20. 作业一:编程设计电影海报的色彩

热门文章

  1. 一对多关系(one-to-many)
  2. SpringSecurity 权限控制之开启动态权限注解支持
  3. 自定义ChannelHandler 的添加过程
  4. AbstractAutowireCapableBeanFactory 类对容器生成的Bean 添加后置处理器
  5. String数据类型的应用场景
  6. ThreadLocal的空指针异常问题
  7. 走过的弯路,你的套路
  8. GraphQL入门之graphql-java项目的介绍
  9. HTTP_请求消息_请求头请求体
  10. Eureka深入理解