NOIP2017赛前模拟(3):总结
题目:
1.购买板凳(100)
大意:区间修改最后查询全局最大值;
2.新排序(40分暴力)
大意:给一串长度小于100000的数列···每次操作找出序列中严格小于其左边的数字或者严格大于其右边的数字然后删除直到无法操作···最后要求输出最后的序列
3.豆豆游戏(5分dp······)
大意:类似与zuma游戏··给定一个长度小于200的01串··每次往串中插入0或者1构成连续3个相等数字的串的话就能消除····问最少插入多少个0或者1可以消除整个串···注意有连锁反应
题解:
1.差分
略
2.模拟
当然不是纯n方模拟···举个极端例子:50001,50002······100000,1,2,3,4······50000···直接模拟的话就是n方的复杂度··
考虑我们每次删除一对数···每次会影响的只有删除的数列的两端的数···比如上述例子一来我们会删除100000,1,那么受影响的只有99999,2因此我们直接将99999和2加入到另一个队列里面····下次直接考虑新建的队列即可·····
然而考试的时候直接打的n方暴力····其实如果我举出了上述例子还是比较容易想到正解的·····下次题做不出来多举例子试试··
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; const int inf=0x3f3f3f3f; bool in[N],del[N]; int T,n,a[N],cnt; struct node { int pos,val; }b[N]; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } int main() { //freopen("a.in","r",stdin); T=R(); while(T--) { memset(del,false,sizeof(del)); memset(in,false,sizeof(in)); n=R();bool flag=false;cnt=0; for(int i=1;i<=n;i++) a[i]=R();a[0]=-inf,a[n+1]=inf; for(int i=1;i<=n;i++) if(a[i]>a[i+1]||a[i]<a[i-1]) del[i]=true,flag=true; for(int i=1;i<=n;i++) { if(del[i]) { if(i>1&&!del[i-1]&&!in[i-1]) b[++cnt]=(node){i-1,a[i-1]},in[i-1]=true; if(i<n&&!del[i+1]&&!in[i+1]) b[++cnt]=(node){i+1,a[i+1]},in[i+1]=true; } } while(flag) { b[0].val=-inf,b[cnt+1].val=inf; flag=false;int temp=0; for(int i=1;i<=cnt;i++) { in[b[i].pos]=false; if(b[i].val>b[i+1].val||b[i].val<b[i-1].val) del[b[i].pos]=true,flag=true; } for(int i=1;i<=cnt;i++) { int p=b[i].pos; if(del[p]) { if(p>1&&!del[p-1]&&!in[p-1]) b[++temp]=(node){p-1,a[p-1]},in[p-1]=true; if(p<n&&!del[p+1]&&!in[p+1]) b[++temp]=(node){p+1,a[p+1]},in[p+1]=true; } else if(!in[p]) b[++temp]=b[i],in[p]=true; } cnt=temp; } int ans=0; for(int i=1;i<=n;i++) if(!del[i]) ans++; cout<<ans<<endl; for(int i=1;i<=n;i++) if(!del[i]) cout<<a[i]<<" "; cout<<endl; } return 0; }
3.区间dp+分类讨论
md因为4种情况只想到3种直接挂到天上去····
先预处理出每一段的颜色以及数目··考虑f[i][j]表示消除ij段的最小代价···我们可以分成下面4种情况:
第一种:当i==j时:
f[i][i]=3-num[i]
其中num[i]为这i段的数目.
第二种:当col[i]!=col[j]时:
f[i][j]=f[i][k]+f[k+1][j]
其中i<=k<j,col[i]表示i段的颜色 .
第三种:当col[i]=col[j]时:
f[i][j]=min(f[i][j],f[i+1][j-1]+max(0,3-num[i]-num[j]))
第四种:当col[i]=col[j]时:
f[i][j]=min(f[i][j],f[i+1][k-1]+f[k+1][j-1])
其中col[i]=col[j]=col[k]且num[i]+num[j]<4且num[k]=1
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; const int inf=0x3f3f3f3f; bool in[N],del[N]; int T,n,a[N],cnt; struct node { int pos,val; }b[N]; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } int main() { //freopen("a.in","r",stdin); T=R(); while(T--) { memset(del,false,sizeof(del)); memset(in,false,sizeof(in)); n=R();bool flag=false;cnt=0; for(int i=1;i<=n;i++) a[i]=R();a[0]=-inf,a[n+1]=inf; for(int i=1;i<=n;i++) if(a[i]>a[i+1]||a[i]<a[i-1]) del[i]=true,flag=true; for(int i=1;i<=n;i++) { if(del[i]) { if(i>1&&!del[i-1]&&!in[i-1]) b[++cnt]=(node){i-1,a[i-1]},in[i-1]=true; if(i<n&&!del[i+1]&&!in[i+1]) b[++cnt]=(node){i+1,a[i+1]},in[i+1]=true; } } while(flag) { b[0].val=-inf,b[cnt+1].val=inf; flag=false;int temp=0; for(int i=1;i<=cnt;i++) { in[b[i].pos]=false; if(b[i].val>b[i+1].val||b[i].val<b[i-1].val) del[b[i].pos]=true,flag=true; } for(int i=1;i<=cnt;i++) { int p=b[i].pos; if(del[p]) { if(p>1&&!del[p-1]&&!in[p-1]) b[++temp]=(node){p-1,a[p-1]},in[p-1]=true; if(p<n&&!del[p+1]&&!in[p+1]) b[++temp]=(node){p+1,a[p+1]},in[p+1]=true; } else b[++temp]=b[i],in[p]=true; } cnt=temp; } int ans=0; for(int i=1;i<=n;i++) if(!del[i]) ans++; cout<<ans<<endl; for(int i=1;i<=n;i++) if(!del[i]) cout<<a[i]<<" "; cout<<endl; } return 0; }