题目大意:
  一个$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]サンドイッチ的更多相关文章

随机推荐

  1. 一个初学者的辛酸路程-依旧Django

    回顾: 1.Django的请求声明周期?   请求过来,先到URL,URL这里写了一大堆路由关系映射,如果匹配成功,执行对应的函数,或者执行类里面对应的方法,FBV和CBV,本质上返回的内容都是字符串 ...

  2. mysql语法结构

    环境:win7 64位.mysql 适合阅读者:对sql基本语法有一定了解 <建表语句>: create table <表名>( <列名> <类型> & ...

  3. Vue打包app

    前言 公司之前用的app就是一个套壳挂个链接就能用的app,后来需要添加微信分享方便传播,没办法只好做成混合式的app了, 因为之前做.net用vs可以创建cordova项目也试着玩过,就决定用cor ...

  4. 团队Alpha(八)冲刺

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  5. 第八章 Internet控制报文协议

    Internet控制报文协议 首先,我们必须先清楚,IP协议本身没有为终端系统提供直接的方法来发现那些发往目的地址失败的IP数据包,并且IP没有提供直接的方式来获取诊断信息,那么我们的故事来了. In ...

  6. .net网站数据抓取

    最新项目需要抓取人民币汇率中间价的数据,所以就写了个简单的爬虫抓取数据.抓取的网站为:http://www.safe.gov.cn/wps/portal/sy/tjsj_hlzjj_inquire # ...

  7. nginx索引目录配置

    为了简单共享文件,有些人使用svn,有些人使用ftp,但是更多得人使用索引(index)功能.apache得索引功能强大,并且也是最常见得,nginx得auto_index实现得目录索引偏少,而且功能 ...

  8. Qt编程的一些技巧

    1.Qt程序在运行过程中,调用函数(如lcdNumber->display(num))显示数据到界面上时,并不会马上刷新屏幕显示,而是要等主程序运行到函数a.exec()时,才刷新屏幕,如下 因 ...

  9. Python中的返回函数与闭包

    返回函数,顾名思义,就是高阶函数可以把函数作为return值返回.与闭包的关系是:闭包需要以返回函数的形式实现. 一. 返回函数 比如我们有一个求和函数: >>> def calc_ ...

  10. 【GXZ的原创】平衡树性能测试

    本文作者为 GXZlegend ,转载请注明 出处 ,谢谢! 〇.序言 前些日子闲的蛋疼做了个平衡树性能测试... 主要是因为学会的平衡树越来越多,做题时却不知道写哪个... 本想结合效率和代码复杂度 ...