bzoj 2824: [AHOI2012]铁盘整理
来源:互联网 发布:淘宝皮草品牌 编辑:程序博客网 时间:2024/04/30 05:51
题目描述
输入输出格式
输入格式:共两行。第一行为铁盘个数N(1<=N<=50),第二行为N个不同的正整数,分别为从上到下的铁盘的半径R。(1<=R<=100)
输出格式:一个正整数,表示使铁盘从小到大有序需要的最少翻转次数。
输入输出样例
输入样例#1:
52 4 3 5 1
输出样例#1:
5
#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<queue>#define inf 999999999#define For(i,a,b) for(i=a;i<=b;++i)#define rep(i,a,b) for(i=a;i>=b;--i)#define mm(a,b) memset(a,b,sizeof(a))#define ll long longusing namespace std;int read(){ int sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar(); return sum*flag;}int maxx(int x,int y){ if(x<y)return y; return x;}int minn(int x,int y){ if(x<y)return x; return y;}int abss(int x){ if(x>=0)return x; return -x;}const int maxn = 100010;int a[51],b[51];struct node{ int s[51],t;};int n,cnt;node tmp;void bfs(){ int i,j; node T; memcpy(T.s,a,sizeof(a)); T.t=0; queue<node>q; q.push(T); while(!q.empty()){ T=q.front(); q.pop(); int jicun=n; while(T.s[jicun]==b[jicun])jicun--; For(i,2,jicun){ memcpy(tmp.s,T.s,sizeof(T.s)); int k=i>>1; For(j,1,k){//直接翻转 int ch=tmp.s[j]; tmp.s[j]=tmp.s[i-j+1]; tmp.s[i-j+1]=ch; } tmp.t=T.t+1; if(memcmp(tmp.s,b,sizeof(b))==0){//如果到达目标状态则结束 printf("%d\n",T.t+1); return ; } q.push(tmp); } }}int main(){ int i,j; n=read(); For(i,1,n){ a[i]=read(); b[i]=a[i]; } sort(b+1,b+n+1); if(memcmp(a,b,sizeof(b))==0){ printf("1\n"); return 0; } bfs(); return 0;}我发现是状态太多的原因,于是想用trie判重,但是数组小一点便re,大一点就mle,还是十分,仅贴一个程序作为纪念:
#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<queue>#define inf 999999999#define For(i,a,b) for(i=a;i<=b;++i)#define rep(i,a,b) for(i=a;i>=b;--i)#define mm(a,b) memset(a,b,sizeof(a))#define ll long longusing namespace std;int read(){ int sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar(); return sum*flag;}int maxx(int x,int y){ if(x<y)return y; return x;}int minn(int x,int y){ if(x<y)return x; return y;}int abss(int x){ if(x>=0)return x; return -x;}const int maxn = 100010;int a[51],b[51];struct node{ int s[51],t;};int n,cnt;int trie[2000000][51];node tmp;int pd(){//字典树部分 int i,u=0,flag=0; For(i,1,n){ if(!trie[u][tmp.s[i]]){ flag=1; trie[u][tmp.s[i]]=++cnt; } u=trie[u][tmp.s[i]]; } if(flag)return 1; return 0;}void bfs(){ int i,j; node T; memcpy(T.s,a,sizeof(a)); T.t=0; queue<node>q; q.push(T); while(!q.empty()){ T=q.front(); q.pop(); For(i,2,n){ memcpy(tmp.s,T.s,sizeof(T.s)); int k=i>>1; For(j,1,k){ int ch=tmp.s[j]; tmp.s[j]=tmp.s[i-j+1]; tmp.s[i-j+1]=ch; } if(pd()){//判重 tmp.t=T.t+1; if(memcmp(tmp.s,b,sizeof(b))==0){ printf("%d\n",T.t+1); return ; } q.push(tmp); } } }}int main(){ int i,j; n=read(); For(i,1,n){ a[i]=read(); b[i]=a[i]; } sort(b+1,b+n+1); if(memcmp(a,b,sizeof(b))==0){ printf("1\n"); return 0; } bfs(); return 0;}我开始认真对待这道题,一些题解里说最多可以只翻2*n次但都没有解释清楚,但身为蒟蒻的我一时半会儿想不通,以下就写出证明过程以供其他蒟蒻参考:
每一次确定最末尾的那个数,步骤是第一步,把最末尾未确定的数换到第一个位置,接下来就可以换到最后了,接下来一样的步骤,举个例子:
对于 2 4 3 5 1,最开始要确定最后一位,也就是要把最后一位变成5,于是先把5放到第一位,->5 3 4 2 1,再换到最后1 2 4 3 5,之后5就不管他了
只剩下1 2 4 3,于是一样的->4 2 1 3->3 1 2 4,还有3个数,3 1 2->2 1 3->1 2 3于是就排好了,算下来,最坏情况只需要2*n-1步
于是就有了第一个剪枝(大于2*n-1的情况可以直接减掉),然而有了这个剪枝还是没有什么用,我们还要利用另外一个原理:若两个数在目前位置上相邻,但在目标位置上不相邻,则因为是翻转,因此至少需要一步把这两个数分开,因此从当前排列到目标排列最少就需要出现这种情况的总和。待会代码里还会解释:
#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<queue>#define inf 999999999#define For(i,a,b) for(i=a;i<=b;++i)#define rep(i,a,b) for(i=a;i>=b;--i)#define mm(a,b) memset(a,b,sizeof(a))#define ll long longusing namespace std;int read(){ int sum=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();} while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar(); return sum*flag;}int maxx(int x,int y){ if(x<y)return y; return x;}int minn(int x,int y){ if(x<y)return x; return y;}int abss(int x){ if(x>=0)return x; return -x;}const int maxn = 100010;struct node{int value,pos,nowpos;};node a[maxn];bool cmp(node c,node d){return c.value<d.value;}int ans,n;void dfs(int sum,int total){//sum是已走过的步数,total是当前排列到达目标状态的预计步数if(!total&&a[1].nowpos==1){ans=minn(ans,sum);return;}//若目标步数为0,且第一个位置上是对的,这是为了预防如5 4 3 2 1这种情况的出现int i,j;For(i,2,n){//枚举翻前i位int zong=total;if(i<n){//如果i位置与i+1位置相邻且预计位置不相同,则可以通过这步操作分开他们,同时预计步数减少,但要考虑换过来后第1个数和第i+1个数的位置关系zong-=(abss(a[i].nowpos-a[i+1].nowpos)!=1)-(abss(a[1].nowpos-a[i+1].nowpos)!=1);}if(zong+sum<ans){//若当前步数加上预计步数大于已搜到的,就剪掉int mid=i/2;For(j,1,mid){int tmp=a[j].nowpos;a[j].nowpos=a[i-j+1].nowpos;a[i-j+1].nowpos=tmp;}dfs(sum+1,zong);For(j,1,mid){//回溯int tmp=a[j].nowpos;a[j].nowpos=a[i-j+1].nowpos;a[i-j+1].nowpos=tmp;}}}}int main(){int i,j;n=read();For(i,1,n){a[i].value=read();a[i].pos=i;}sort(a+1,a+n+1,cmp);For(i,1,n){a[a[i].pos].nowpos=i;//把当前位置的目标位置记录}int sum=0;ans=n*2-1;//至多搜n*2-1次For(i,2,n){sum+=abss(a[i].nowpos-a[i-1].nowpos)!=1;//如果当前排列中位置相邻但目标排列中并不相邻则预计步数加1}dfs(0,sum);printf("%d\n",ans);return 0;}剪枝万岁!
2 0
- bzoj 2824: [AHOI2012]铁盘整理
- bzoj 2824: [AHOI2012]铁盘整理
- [BZOJ]2824: [AHOI2012]铁盘整理 dfs
- 2824: [AHOI2012]铁盘整理
- bzoj2824: [AHOI2012]铁盘整理
- BZOJ2824 洛谷P2534 [AHOI2012]铁盘整理
- 【BZOJ 2823】 [AHOI2012]信号塔
- BZOJ 2823 AHOI2012 信号塔 计算几何
- 【BZOJ 2822】 [AHOI2012]树屋阶梯
- 【BZOJ 2823】[AHOI2012]信号塔 随机增量
- BZOJ 2822: [AHOI2012]树屋阶梯
- 【BZOJ】【P2822】【AHOI2012】【树屋阶梯】【Catalan数+高精度】
- BZOJ 2822 AHOI2012 树屋阶梯 卡特兰数
- BZOJ 2823: [AHOI2012]信号塔 随机增量法
- bzoj 2822 [AHOI2012]树屋阶梯 卡特兰数
- bzoj 2822: [AHOI2012]树屋阶梯 (卡特兰数+高精度)
- BZOJ 2823 [AHOI2012]信号塔 (最小圆覆盖学习笔记)
- BZOJ 题目整理
- test
- 无线电频谱和波段划分
- vim E303
- UPDATE & DELETE (For MySQL)
- sql中的dbl的含义
- bzoj 2824: [AHOI2012]铁盘整理
- 灰度图像的直方均衡、线性变换与线性拉伸
- 使用线程接收串口接收数据,DLT645 2007 智能电表抄表代码
- CCF201612-1-中间数
- centos7下的NFS 服务器端的搭建
- Python
- 微信JSAPI模式与浏览器类型安全访问
- windows下启动openoffice服务
- Floyed那些事~~~~~