洛谷 1155 (NOIp2008)双栈排序——仔细分析不合法的条件
题目:https://www.luogu.org/problemnew/show/P1155
这道题教会我们要多思考。
好好分析过后发现同一个栈里不能有升序。就用它写了一个30分。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
int n,stack[][N],top[],nw,tot,ps[N];
char ch[N<<];
bool flag;
int main()
{
scanf("%d",&n);int x;nw=;
stack[][]=stack[][]=n+;
for(int i=;i<=n;i++)
{
scanf("%d",&x);
if(stack[][top[]]>x){
stack[][++top[]]=x;
ch[++tot]='a';ps[x]=;
}
else if(stack[][top[]]>x){
stack[][++top[]]=x;
ch[++tot]='c';ps[x]=;
}
else {
flag=;break;
}
while(ps[nw])
{
if(ps[nw]==){
top[]--;ch[++tot]='b';
}
else{
top[]--;ch[++tot]='d';
}
nw++;
}
}
if(flag)printf("");
else for(int i=;i<=tot;i++)printf("%c ",ch[i]);
return ;
}
然后发现只有30分。想想自己想得太简单了。于是写了个深搜30分。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
int n,stack[][N],top[],tot,ps[N],a[N];
char ch[N<<];
bool flag;
void rd(int x,int k)
{
stack[k][++top[k]]=x;ch[++tot]=(k?'c':'a');ps[x]=k+;
// printf("stack%d=%d(top[k]=%d)\n",k,stack[k][top[k]],top[k]);
}
void clr(int x,int k)
{
top[k]--;tot--;ps[x]=;
}
void cd(int &nw)
{
// printf("cd!\n");
while(ps[nw])
{
if(ps[nw]==){
top[]--;ch[++tot]='b';
}
else{
top[]--;ch[++tot]='d';
}
nw++;
}
}
void dfs(int cr,int nw)
{
if(cr>n)return;
int x=a[cr],tnw=nw;bool cflag=;
int tsk[][N];memcpy(tsk,stack,sizeof stack);
if(stack[][top[]]>x&&stack[][top[]]<x)
{
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d 0\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);
}
clr(x,);
// printf("ret cr=%d flag=1\n",cr);
}
}
else if(stack[][top[]]>x&&stack[][top[]]<x)
{
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d 1\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);
}
clr(x,);
// printf("ret cr=%d flag=1\n",cr);
}
}
else if(stack[][top[]]<x&&stack[][top[]]<x)
{
// printf("cr=%d flag=1!\n",cr);
flag=;return;
}
else{
// printf("stack0=%d stack1=%d(top0=%d top1=%d)\n",stack[0][top[0]],stack[1][top[1]],top[0],top[1]);
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d try0\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);
}
clr(x,);
// printf("cr=%d failtry0 try1\n",cr);
flag=;cflag=;rd(x,);
if(ps[nw])cflag=,cd(nw);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);
}
clr(x,);
// printf("cr=%d failtry1\n",cr);
}
}
}
}
int main()
{
scanf("%d",&n);int x;
stack[][]=stack[][]=n+;
for(int i=;i<=n;i++)scanf("%d",&a[i]);
dfs(,);
if(flag)printf("");
else for(int i=;i<=tot;i++)printf("%c ",ch[i]);
return ;
}
然后发现只有30分。那就剪剪枝吧。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
int n,stack[][N],top[],tot,ps[N],a[N];
char ch[N<<];
bool flag;
void rd(int x,int k)
{
stack[k][++top[k]]=x;ch[++tot]=(k?'c':'a');ps[x]=k+;
// printf("stack%d=%d(top[k]=%d)\n",k,stack[k][top[k]],top[k]);
}
void clr(int x,int k)
{
top[k]--;tot--;ps[x]=;
}
void cd(int &nw)
{
// printf("cd!\n");
while(ps[nw])
{
if(ps[nw]==){
top[]--;ch[++tot]='b';
}
else{
top[]--;ch[++tot]='d';
}
nw++;
}
}
bool check(int cr,int x)
{
int i;
for(i=cr+;i<=n;i++)if(a[i]>a[cr]&&a[i]>x)break;
for(int j=i+;j<=n;j++)if(a[j]<a[cr])return ;//导致在j之前cr不能弹出
return ;
}
void dfs(int cr,int nw)
{
if(cr>n)return;
int x=a[cr],tnw=nw;bool cflag=;
int tsk[][N];memcpy(tsk,stack,sizeof stack);
if(stack[][top[]]>x&&stack[][top[]]<x)
{
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d 0\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);cflag=;
}
clr(x,);
// printf("ret cr=%d flag=1\n",cr);
}
}
else if(stack[][top[]]>x&&stack[][top[]]<x)
{
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d 1\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);cflag=;
}
clr(x,);
// printf("ret cr=%d flag=1\n",cr);
}
}
else if(stack[][top[]]<x&&stack[][top[]]<x)
{
// printf("cr=%d flag=1!\n",cr);
flag=;return;
}
else{
// printf("stack0=%d stack1=%d(top0=%d top1=%d)\n",stack[0][top[0]],stack[1][top[1]],top[0],top[1]);
if(check(cr,stack[][top[]]))
{
rd(x,);
if(ps[nw])cflag=,cd(nw);
// printf("cr=%d try0\n",cr);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);cflag=;
}
clr(x,);
}
}
if(flag&&check(cr,stack[][top[]]))
{
flag=;rd(x,);
if(ps[nw])cflag=,cd(nw);
dfs(cr+,nw);
if(flag){
if(cflag){
tot-=nw-tnw;top[]+=nw-tnw;nw=tnw;
memcpy(stack,tsk,sizeof tsk);cflag=;
}
clr(x,);
// printf("cr=%d failtry1\n",cr);
}
}
if(flag||(!check(cr,stack[][top[]])&&!check(cr,stack[][top[]])))flag=;
}
}
int main()
{
scanf("%d",&n);int x;
stack[][]=stack[][]=n+;
for(int i=;i<=n;i++)scanf("%d",&a[i]);
dfs(,);
if(flag)printf("");
else for(int i=;i<=tot;i++)printf("%c ",ch[i]);
return ;
}
然后发现只有30分。而且T1WA6。
在写搜索之前看了看TJ,体现在最后那个check里。就是如果a[1],a[2],a[3]满足a[2]>a[1],a[3]<a[1]的话,在a[3]来之前a[1]不能出栈,就被堵在了a[2]后面,从而GG。
但是这竟然就是唯一的GG条件!需要仔细思考。
这个性质和自己开始分析出来的浅显性质的不同之处:
1.自己的那个只有在过程中才能判断,因为一开始有升序,也可能前面小的先出栈,后面大的再进来。
而正解的想法是把这个不确定因素也分析掉,得出如果在这个大的后面又有一个比最前面的更小的,就真的不行了。
这样就能不在过程中判断而可以预先判断了。
2.自己对“两个栈”认识不足。明明可以把它看成一个主栈和一个辅助栈,用上一条判断出不能往主栈放就放在辅助栈里,可自己……
没错,正解这样想的话就可以只关注元素能不能放在同一个栈中。然后想到只有两个栈,于是二分图染色就有了。
因为判掉了无解,所以最后的模拟可以开放又随便。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
int n,a[N],head[N],xnt,col[N],ps[N],now,f[N];
bool flag;
struct Edge{
int next,to;
Edge(int n=,int t=):next(n),to(t) {}
}edge[N*N];
void add(int x,int y)
{
edge[++xnt]=Edge(head[x],y);head[x]=xnt;
edge[++xnt]=Edge(head[y],x);head[y]=xnt;
}
void init()
{
f[n]=n+;
for(int i=n-;i;i--)
{
f[i]=min(f[i+],a[i+]);
for(int j=i+;j<=n;j++)
if(a[j]>a[i]&&f[j]<a[i])add(i,j);
}
}
void dfs(int cr)
{
for(int i=head[cr],v;i;i=edge[i].next)
{
if(col[v=edge[i].to]==col[cr])
{flag=;return;}
else if(!col[v])col[v]=(col[cr]^),dfs(v);
if(flag)return;
}
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
init();
for(int i=;i<=n;i++) if(!col[i])
{
col[i]=;dfs(i);if(flag)break;
}
if(flag){
printf("");return ;
}
now=;
for(int i=;i<=n;i++)
{
ps[a[i]]=col[i];
if(col[i]==)printf("a ");
else printf("c ");
while(ps[now]){
if(ps[now++]==)printf("b ");
else printf("d ");
}
}
return ;
}
从代码的col和ps可以看出二分图染色和最后的模拟有一定程度的重复。其实可以大胆地直接模拟就行了。
(此方法源自洛谷用户cmd2001。在洛谷题解的第2页可以看到。)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=;
int n,r[N],ct,now,a[N],ta,b[N],tb,tot;
char ch[N<<];
bool flag;
bool check(int cr)
{
int i;
for(i=cr+;i<=n;i++)if(r[i]>r[cr]&&r[i]>b[tb])break;
for(int j=i+;j<=n;j++)if(r[j]<r[cr])return ;
return ;
}
int main()
{
scanf("%d",&n);int lm=(n<<);now=;ct=;
for(int i=;i<=n;i++)scanf("%d",&r[i]);
a[]=b[]=n+;
for(int i=;i<=lm;i++)
{
if(a[ta]==now)
{
ch[++tot]='b';ta--;now++;
continue;
}
if(b[tb]==now)
{
ch[++tot]='d';tb--;now++;
continue;
}
if(ct<=n&&r[ct]<a[ta]&&check(ct))
{
a[++ta]=r[ct++];ch[++tot]='a';
continue;
}
if(ct<=n&&r[ct]<b[tb])
{
b[++tb]=r[ct++];ch[++tot]='c';
continue;
}
flag=;break;
}
if(flag)printf("");
else for(int i=;i<=lm;i++)printf("%c ",ch[i]);
return ;
}
这道题教会我们要好好分析性质。别只分析出一个浅显的了!
洛谷 1155 (NOIp2008)双栈排序——仔细分析不合法的条件的更多相关文章
- Luogu1155 NOIP2008 双栈排序 【二分图染色】【模拟】
Luogu1155 NOIP2008 双栈排序 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过 2个栈 S1 和 S2 ,Tom希望借助以下 44 种操作实现将输入序列升序排序. 操作 ...
- NOIP2008双栈排序[二分图染色|栈|DP]
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- noip2008 双栈排序
题目描述 Description \(Tom\)最近在研究一个有趣的排序问题.如图所示,通过\(2\)个栈\(S_1\)和\(S_2\),\(Tom\)希望借助以下\(4\)种操作实现将输入序列升序排 ...
- Noip2008双栈排序
[问题描述] 用两个栈使一个1...n的排列变得有序.一共有四个操作: A.stack1.push() 读入一个放入栈一 B.stack1.pop() 弹出栈一放入输出序列 C.stack2.push ...
- NOIP2008双栈排序(贪心)
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法
Problem 题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操 ...
- [NOIP2008]双栈排序 【二分图 + 模拟】
题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...
- [luogu1155 NOIP2008] 双栈排序 (二分图染色)
传送门 Description Input 第一行是一个整数 n . 第二行有 n 个用空格隔开的正整数,构成一个 1−n 的排列. Output 共一行,如果输入的排列不是"可双栈排序排列 ...
- $[NOIp2008]$双栈排序 栈/二分图/贪心
\(Sol\) 先考虑单栈排序,怎么样的序列可以单栈排序呢?设\(a_i\)表示位置\(i\)是哪个数.\(\exist i<j<k\),都没有\(a_k<a_i<a_j\), ...
随机推荐
- Deep Auto-encoder
autoencoder可以用于数据压缩.降维,预训练神经网络,生成数据等等. autoencoder的架构 autoencoder的架构是这样的: 需要分别训练一个Encoder和一个Decoder. ...
- [BZOJ2342]双倍回文
对每个大中心暴力找小中心即可. 代码: #include<iostream> #include<cstdio> #include<cstring> #define ...
- VC++6.0调试简单快捷键
编译——F7 重新编译——Ctrl+F7 设置断点 ——F9 取消断点——F9 删除所有断点——Ctrl+Shift+F9 开始调试——F5 进行下一次调试——F5 停止调试——Shift+F5 逐过 ...
- Synchronize原理
1 普通方法上 2 静态方法上 修饰静态方法内置锁是当前的Class字节码对象 修饰普通方法内置锁是当前类的实例 原理与使用: 从字节码层面解释: 执行同步代码块 monitorenter synch ...
- HTTP Message Handlers in ASP.NET Web API
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers A message ha ...
- shell script语法高亮和自动缩进的配置
编辑/etc/profile文件,在文件末尾加一下内容: export TERM=xterm-color 接着让其变为全局变量 source /etc/profile 编辑/etc/vimrc文件,在 ...
- Java 获取路径的几种方法 - 转载
1.获取当前类所在的“项目名路径” String rootPath = System.getProperty("user.dir"); 2.获取编译文件“jar包路径”(反射) S ...
- vector push_back数量大的时候变慢
才用15000个数据 push_back耗时就好几秒, 解决方法是 先resize 15000, 然后再 for (int i = 0; i < 15000; i++) { Data data ...
- LeetCode第[53]题(Java):Maximum Subarray
题目:和最大的子序列 难度:Medium 题目内容: Given an integer array nums, find the contiguous subarray (containing at ...
- linux的文件
今日感慨:linux根目录下的文件夹含义 bin的知识,二进制文件,其用途依系统或应用而定 . 也就是说,一般来讲是机器代码,汇编语言编译后的结果,(DOS下汇编语言编译后与.com文件相类似),用d ...