[JOISC2016]サンドイッチ
题目大意:
一个$n\times m(n,m\leq400)$的网格图中,每个格子上放了两个三明治,摆放的方式分为'N'和'Z'两种。一个三明治可以被拿走当且仅当与该三明治的两条直角边相邻的三明治均被拿走或与该三明治斜边相邻的三明治被拿走。问对于每个格子,想要拿走这个格子中的所有三明治至少需要先拿走多少三明治。
思路:
对于同一个格子,不难发现这一个格子中两个三明治接连被拿走一定是最优的。
于是这题就和每个单独的三明治取走顺序没什么关系了,只和每个方格取走顺序及三明治的摆放方式有关。
$O(n^2)$枚举每个格子$(x,y)$,假设它是因为$(x-1,y)$和$(x,y-1)$被取走后才被取走,我们可以$O(n^2)$DFS出最优情况下,取走每个格子之前一定要取走哪些格子。时间复杂度$O(n^4)$,bitset优化为$O(\frac{n^4}{\omega})$。
不难发现,若$(x,y)$是因为$(x-1,y)$被取走才被取走的,$(x-1,y)$不可能因为$(x,y)$被取走才被取走。因此对于同一行的格子,我们可以让后面的DFS重复利用前面DFS出的信息。DFS是$O(n^2)$的,每一行要重新DFS,时间复杂度是$O(n^3)$。
具体实现上,可以用$0\sim3$来表示不同的方向。若摆放方式为'N'的格子,一个直角边的方向为$d$,则另一个直角边的方向为$d\oplus3$;若摆放方式为'Z'的格子,一个直角边的方向为$d$,则另一个直角边的方向为$d\oplus1$。搜索时的一系列分类讨论可以通过简单的位运算实现。
#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
inline bool getblock() {
register char ch;
while(!isalpha(ch=getchar()));
return ch=='N';
}
const int N=,inf=0x7fffffff;
const int dx[]={,-,,},dy[]={-,,,};
bool a[N][N];
int n,m,f[N][N],ans[N][N],tmp,flag;
inline bool check(const int &x,const int &y) {
return x>=&&x<n&&y>=&&y<m;
}
void dfs(const int &x,const int &y,const int &d) {
if(!~f[x][y]) {
flag=true;
return;
}
if(f[x][y]) return;
tmp+=;
f[x][y]=-;
const int p=a[x][y]?:;
if(check(x-dx[d],y-dy[d])) dfs(x-dx[d],y-dy[d],d);
if(check(x-dx[d^p],y-dy[d^p])) dfs(x-dx[d^p],y-dy[d^p],d^p);
f[x][y]=;
}
int main() {
for(register int T=getint();T;T--) {
n=getint(),m=getint();
for(register int i=;i<n;i++) {
for(register int j=;j<m;j++) {
a[i][j]=getblock();
}
}
for(register int i=;i<n;i++) {
flag=tmp=;
for(register int i=;i<n;i++) {
for(register int j=;j<m;j++) {
f[i][j]=;
}
}
for(register int j=;j<m;j++) {
if(!flag) dfs(i,j,);
ans[i][j]=flag?inf:tmp;
}
flag=tmp=;
for(register int i=;i<n;i++) {
for(register int j=;j<m;j++) {
f[i][j]=;
}
}
for(register int j=m-;~j;j--) {
if(!flag) dfs(i,j,);
ans[i][j]=std::min(ans[i][j],flag?inf:tmp);
}
}
for(register int i=;i<n;i++) {
for(register int j=;j<m;j++) {
printf("%d%c",ans[i][j]!=inf?ans[i][j]:-," \n"[j==m-]);
}
}
}
return ;
}
[JOISC2016]サンドイッチ的更多相关文章
随机推荐
- Pytest+allure生成测试报告
1.Allure.zip包的下载地址: https://github.com/allure-framework/allure2 在跳转页面选择一个allure.zip包的版本下载 若以上方法无法下载z ...
- JNDI和JDBC的区别和联系及其使用方法
一.JNDI 和JDBC的区别和联系 两者都是API,是连接数据库的标准.并不是什么产品或方法. 二.JDBC 全称:Java Database Connectivity 以一种统一的方式来对各种各样 ...
- cloud-init简介及组件说明
http://cloudinit.readthedocs.io/en/latest/topics/examples.html介绍: cloud-init是专为云环境中虚拟机的初始化而开发的工具, ...
- nodejs取参四种方法req.body,req.params,req.param,req.body
摘要: nodejs取参四种方法req.body,req.params,req.param,req.body 获取请求很中的参数是每个web后台处理的必经之路,nodejs提供了四种方法来实现. 获取 ...
- php 链接转二维码图片
// 类库下载地址 https://sourceforge.net/projects/phpqrcode/files/ $value = 'www.baidu.com';//二维码内容 $errorC ...
- Android记事本08
昨天: Anr问题异常的原因和解决方案. 今天: Activity数据传递之通用方式. Activity数据传递之静态变量. Activity数据传递之全局变量. 遇到的问题: 无.
- c# json 反序列化 泛型List 2行代码
List<EncyTable> list = new List<EncyTable>(); var jsonReqeust = "[{ENCY_ID:775,ENCY ...
- IO多路复用的理解
最近看了<后台开发核心技术与应用实践>有关select.poll和epoll部分以及相关的一些博客,学习了这三个函数的使用方法和区别,写一个易理解的总结. IO多路复用 之前程序中使用的I ...
- yii2.0查询慢的原因
最近使用Yii2.0来搭建项目,测试的时候发现无论是请求列表数据还是发布数据,都很慢,然后我一步一步打印时间来查看哪里的问题,始终找不到原因,最后在网上看到这篇: 'db' => [ 'clas ...
- WCF信道绑定代码
监听端创建信道Listener,代码 using System; using System.Collections.Generic; using System.Linq; using System.T ...