[NOIP模拟][链表]裁剪表格
2017-10-20 21:13
232 查看
题目描述:
题目大意:
给一个n*m的矩阵,每个格子都有一个数字v,每次交换两个大小相同的不重叠的子矩阵,输出最后的矩阵。
输入格式:
第一行三个整数n,m,q代表表格的行数和列数和操作次数;
接下来n行,每行m个整数,表示格中的数字。
接下来q行,每行六个数字,r1,c1,r2,c2,h,w,分别表示第一个矩形左上角所在行、所在列,第二个矩形左上角所在行、所在列,这两个矩形的高度和宽度(保证着两个矩形都在原有表格内)。
输出格式:
输出n行,每行m个整数,表示最后的矩阵。
样例输入:
4 4 2
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
1 1 3 3 2 2
3 1 1 3 2 2
样例输出:
4 4 3 3
4 4 3 3
2 2 1 1
2 2 1 1
数据范围:
对于10%的数据,所有的v=1;
对于30%的数据,n,m≤300,q≤300;
对于60%的数据,n,m≤1000,q≤500;
对于100%的数据,2≤n,m≤1000,1≤q≤500;1≤v≤1000000。
题目分析:
先吐槽一下这道题,因为题面中提到高级数据结构,然后有许多人写了splay,出题人为了卡那些人,后面的数据的询问就专门针对splay,结果导致暴力踩标算,直接拿满(暴力就是指直接模拟交换)。后来后面数据改了一下还是有80分,加register还是可以满分。(主要是数据很难兼顾同时卡暴力和splay)。
分析:这道题其实是一道链表题,我们可以记录四个方向(上下左右),每次交换,其实只用修改两个矩形的四周那一圈的连接。于是时间复杂度O(n*q)。
具体操作:先从起点(初始为(1,1),如果(1,1)与其它点交换,则这个点就成为了起点(因为实际上它被换到了(1,1)的位置))出发,分别走到这两个矩形的左上角,然后绕矩形走一圈(在两个矩形上的点是对应着一起走),每次交换两个矩形对应点连出去的指向和外面被连接的点所连回来的指向。这样就相当于交换整个矩形。
还可以优化:只记录两个指向,右、下,因为你输出只根据向右和向下就可以输出,而向右和向下就可以表示4个方向(上面的情况是在双向修改,这里就是单向的)。
链表可以一般写法,也可以指针写。
下附4份代码:1、暴力,2、我写的记录四个方向的链表,3、标算的记录两个方向的链表,4、记录两个方向的链表的指针写法。
附代码:
1、暴力+register:
是我考试代码直接加的register,所以还有特判部分
2、记录四个方向的链表:
我之所以把这份代码发在前面,只是因为是我自己写的且耗时很久。因为维护4个方向,写起来复杂,写完后又因为一些情况没考虑,或则手抖写错某个地方,于是调试了很久。
3、标算的记录两个方向的链表:
简洁,推荐。
4、记录两个方向的链表的指针写法:
简洁优美。
题目大意:
给一个n*m的矩阵,每个格子都有一个数字v,每次交换两个大小相同的不重叠的子矩阵,输出最后的矩阵。
输入格式:
第一行三个整数n,m,q代表表格的行数和列数和操作次数;
接下来n行,每行m个整数,表示格中的数字。
接下来q行,每行六个数字,r1,c1,r2,c2,h,w,分别表示第一个矩形左上角所在行、所在列,第二个矩形左上角所在行、所在列,这两个矩形的高度和宽度(保证着两个矩形都在原有表格内)。
输出格式:
输出n行,每行m个整数,表示最后的矩阵。
样例输入:
4 4 2
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
1 1 3 3 2 2
3 1 1 3 2 2
样例输出:
4 4 3 3
4 4 3 3
2 2 1 1
2 2 1 1
数据范围:
对于10%的数据,所有的v=1;
对于30%的数据,n,m≤300,q≤300;
对于60%的数据,n,m≤1000,q≤500;
对于100%的数据,2≤n,m≤1000,1≤q≤500;1≤v≤1000000。
题目分析:
先吐槽一下这道题,因为题面中提到高级数据结构,然后有许多人写了splay,出题人为了卡那些人,后面的数据的询问就专门针对splay,结果导致暴力踩标算,直接拿满(暴力就是指直接模拟交换)。后来后面数据改了一下还是有80分,加register还是可以满分。(主要是数据很难兼顾同时卡暴力和splay)。
分析:这道题其实是一道链表题,我们可以记录四个方向(上下左右),每次交换,其实只用修改两个矩形的四周那一圈的连接。于是时间复杂度O(n*q)。
具体操作:先从起点(初始为(1,1),如果(1,1)与其它点交换,则这个点就成为了起点(因为实际上它被换到了(1,1)的位置))出发,分别走到这两个矩形的左上角,然后绕矩形走一圈(在两个矩形上的点是对应着一起走),每次交换两个矩形对应点连出去的指向和外面被连接的点所连回来的指向。这样就相当于交换整个矩形。
还可以优化:只记录两个指向,右、下,因为你输出只根据向右和向下就可以输出,而向右和向下就可以表示4个方向(上面的情况是在双向修改,这里就是单向的)。
链表可以一般写法,也可以指针写。
下附4份代码:1、暴力,2、我写的记录四个方向的链表,3、标算的记录两个方向的链表,4、记录两个方向的链表的指针写法。
附代码:
1、暴力+register:
是我考试代码直接加的register,所以还有特判部分
#include<iostream> #include<cstring> #include<string> #include<cstdlib> #include<cstdio> #include<ctime> #include<cmath> #include<cctype> #include<iomanip> #include<algorithm> using namespace std; const int N=1e3+100; int n,m,q,a ; bool check; int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } int main() { freopen("table.in","r",stdin); freopen("table.out","w",stdout); n=readint();m=readint();q=readint(); for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) { a[i][j]=readint(); if(a[i][j]!=1) check=true; } if(check==false) { for(register int i=1;i<=n;i++) { for(register int j=1;j<=m;j++) cout<<a[i][j]<<" ";//printf("%d ",a[i][j]); printf("\n"); } return 0; } int r1,c1,r2,c2,h,w; while(q--) { r1=readint();c1=readint(); r2=readint();c2=readint(); h=readint();w=readint(); for(register int i=0;i<h;i++) for(register int j=0;j<w;j++) swap(a[i+r1][j+c1],a[i+r2][j+c2]); } for(register int i=1;i<=n;i++) { for(register int j=1;j<=m;j++) cout<<a[i][j]<<" ";//printf("%d ",a[i][j]); printf("\n"); } return 0; }
2、记录四个方向的链表:
我之所以把这份代码发在前面,只是因为是我自己写的且耗时很久。因为维护4个方向,写起来复杂,写完后又因为一些情况没考虑,或则手抖写错某个地方,于是调试了很久。
#include<iostream> #include<cstring> #include<string> #include<cstdlib> #include<cstdio> #include<ctime> #include<cmath> #include<cctype> #include<iomanip> #include<algorithm> using namespace std; int n,m,q,pos[2],now1[2],now2[2],r1,c1,r2,c2,h,w,dian1[2],dian2[2]; struct node{ int w; int up[2],down[2],left[2],right[2];//例:up[0]是向上连接的点的横坐标,up[1]纵坐标,其余同理,包括now1,now2,pos,dain1,dian2 }map[1010][1010]; int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } int main() { //freopen("table.in","r",stdin); //freopen("table.out","w",stdout); n=readint();m=readint();q=readint(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) map[i][j].w=readint(); for(int i=0;i<=n+1;i++)//从0开始到n+1是把原本矩形的一周外面的点也连一次 for(int j=0;j<=m+1;j++) { map[i][j].up[0]=i-1;map[i][j].up[1]=j; map[i][j].right[0]=i;map[i][j].right[1]=j+1; map[i][j].down[0]=i+1;map[i][j].down[1]=j; map[i][j].left[0]=i;map[i][j].left[1]=j-1; } pos[0]=1;pos[1]=1;//赋初始起点 while(q--) { r1=readint();c1=readint();r2=readint();c2=readint();h=readint();w=readint(); memcpy(now1,pos,8);memcpy(now2,pos,8);//这个函数是把第二个位置的赋给第一个位置,8代表赋的长度,以字节计算,一个int4字节 for(int i=1;i<c1;i++)//向右 //找到矩形的两个左上角顶点 memcpy(now1,map[now1[0]][now1[1]].right,8); for(int i=1;i<r1;i++)//向下 memcpy(now1,map[now1[0]][now1[1]].down,8); for(int i=1;i<c2;i++)//向右 memcpy(now2,map[now2[0]][now2[1]].right,8); for(int i=1;i<r2;i++)//向下 memcpy(now2,map[now2[0]][now2[1]].down,8); if(pos[0]==now1[0]&&pos[1]==now1[1]) pos[0]=now2[0],pos[1]=now2[1];//判断起点是否改变 else if(pos[0]==now2[0]&&pos[1]==now2[1]) pos[0]=now1[0],pos[1]=now1[1]; for(int i=1;i<w;i++)//上边 { memcpy(dian1,map[now1[0]][now1[1]].up,8); memcpy(dian2,map[now2[0]][now2[1]].up,8); swap(map[dian1[0]][dian1[1]].down,map[dian2[0]][dian2[1]].down);//交换指向 swap(map[now1[0]][now1[1]].up,map[now2[0]][now2[1]].up); memcpy(now1,map[now1[0]][now1[1]].right,8);//走点 memcpy(now2,map[now2[0]][now2[1]].right,8); } memcpy(dian1,map[now1[0]][now1[1]].up,8);//在外面再维护一次是因为如矩形某边为3,则只走两次,而维护三次 memcpy(dian2,map[now2[0]][now2[1]].up,8); swap(map[dian1[0]][dian1[1]].down,map[dian2[0]][dian2[1]].down); swap(map[now1[0]][now1[1]].up,map[now2[0]][now2[1]].up); for(int i=1;i<h;i++)//右边 { memcpy(dian1,map[now1[0]][now1[1]].right,8); memcpy(dian2,map[now2[0]][now2[1]].right,8); swap(map[dian1[0]][dian1[1]].left,map[dian2[0]][dian2[1]].left); swap(map[now1[0]][now1[1]].right,map[now2[0]][now2[1]].right); memcpy(now1,map[now1[0]][now1[1]].down,8); memcpy(now2,map[now2[0]][now2[1]].down,8); } memcpy(dian1,map[now1[0]][now1[1]].right,8); memcpy(dian2,map[now2[0]][now2[1]].right,8); swap(map[dian1[0]][dian1[1]].left,map[dian2[0]][dian2[1]].left); swap(map[now1[0]][now1[1]].right,map[now2[0]][now2[1]].right); for(int i=1;i<w;i++)//下边 { memcpy(dian1,map[now1[0]][now1[1]].down,8); memcpy(dian2,map[now2[0]][now2[1]].down,8); swap(map[dian1[0]][dian1[1]].up,map[dian2[0]][dian2[1]].up); swap(map[now1[0]][now1[1]].down,map[now2[0]][now2[1]].down); memcpy(now1,map[now1[0]][now1[1]].left,8); memcpy(now2,map[now2[0]][now2[1]].left,8); } memcpy(dian1,map[now1[0]][now1[1]].down,8); memcpy(dian2,map[now2[0]][now2[1]].down,8); swap(map[dian1[0]][dian1[1]].up,map[dian2[0]][dian2[1]].up); swap(map[now1[0]][now1[1]].down,map[now2[0]][now2[1]].down); for(int i=1;i<h;i++)//左边 { memcpy(dian1,map[now1[0]][now1[1]].left,8); memcpy(dian2,map[now2[0]][now2[1]].left,8); swap(map[dian1[0]][dian1[1]].right,map[dian2[0]][dian2[1]].right); swap(map[now1[0]][now1[1]].left,map[now2[0]][now2[1]].left); memcpy(now1,map[now1[0]][now1[1]].up,8); memcpy(now2,map[now2[0]][now2[1]].up,8); } memcpy(dian1,map[now1[0]][now1[1]].left,8); memcpy(dian2,map[now2[0]][now2[1]].left,8); swap(map[dian1[0]][dian1[1]].right,map[dian2[0]][dian2[1]].right); swap(map[now1[0]][now1[1]].left,map[now2[0]][now2[1]].left); } memcpy(now1,pos,8); for(int i=0;i<n;i++)//输出 { memcpy(now2,now1,8); for(int j=0;j<m;j++) { cout<<map[now2[0]][now2[1]].w<<" "; memcpy(now2,map[now2[0]][now2[1]].right,8); } cout<<endl; memcpy(now1,map[now1[0]][now1[1]].down,8); } return 0; }
3、标算的记录两个方向的链表:
简洁,推荐。
#include<bits/stdc++.h> using namespace std; const int N = 1005; const int MaxN = 1005003; int n,m,q,num,v[MaxN],f[MaxN][5],lab ; inline int Readint(){ register char ch=getchar(); register int x=0; while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=((x+(x<<2))<<1)+ch-'0',ch=getchar(); return x; } inline int getlab(int a,int b){ int p=lab[0][0]; for(register int i=1;i<=a;i++) p=f[p][1]; for(register int i=1;i<=b;i++) p=f[p][0]; return p; } int main(){ // freopen("lx.in","r",stdin); n=Readint(),m=Readint(),q=Readint(); for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) v[lab[i][j]=++num]=Readint(); for(register int i=0;i<=m+1;i++) lab[0][i]=++num,lab[n+1][i]=++num; for(register int i=1;i<=n;i++) lab[i][0]=++num,lab[i][m+1]=++num; for(register int i=0;i<=n;i++) for(register int j=0;j<=m;j++) f[lab[i][j]][0]=lab[i][j+1],f[lab[i][j]][1]=lab[i+1][j]; for(int a,b,c,d,h,w,i=1;i<=q;i++){ a=Readint(),b=Readint(),c=Readint(); d=Readint(),h=Readint(),w=Readint(); register int p1=getlab(a-1,b-1),p2=getlab(c-1,d-1); register int t1,t2,ww,hh; for(t1=p1,t2=p2,ww=1;ww<=w;ww++) swap(f[t1=f[t1][0]][1],f[t2=f[t2][0]][1]); for(hh=1;hh<=h;hh++) swap(f[t1=f[t1][1]][0],f[t2=f[t2][1]][0]); for(t1=p1,t2=p2,hh=1;hh<=h;hh++) swap(f[t1=f[t1][1]][0],f[t2=f[t2][1]][0]); for(ww=1;ww<=w;ww++) swap(f[t1=f[t1][0]][1],f[t2=f[t2][0]][1]); } for(int i=1,p=lab[0][0];i<=n;i++) { for(int j=1,t=p=f[p][1];j<=m;j++) cout<<v[t=f[t][0]]<<" "; cout<<'\n'; } return 0; }
4、记录两个方向的链表的指针写法:
简洁优美。
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f; } const int Maxn=1e3+50; struct node{ node *right,*down; int val; }Pool[Maxn*Maxn],*pool=Pool,*pos[Maxn][Maxn]; inline node* newnode(int val){ ++pool; pool->right=pool->down=NULL; pool->val=val; return pool; } int n,m,q; inline node* findpos(int x,int y){ node *now=pos[0][0]; for(register int i=1;i<=x;i++)now=now->down; for(register int i=1;i<=y;i++)now=now->right; return now; } inline void W(int x){ static int buf[50]; if(!x){putchar('0');return;} if(x<0){putchar('-');x=-x;} while(x){buf[++buf[0]]=x%10;x/=10;} while(buf[0])putchar(buf[buf[0]--]+'0'); } int main(){ n=read(),m=read(),q=read(); pos[0][0]=newnode(0); for(register int i=1;i<=m;i++){ pos[0][i]=newnode(0); pos[0][i-1]->right=pos[0][i]; } for(register int i=1;i<=n;i++){ pos[i][0]=newnode(0); pos[i-1][0]->down=pos[i][0]; for(register int j=1;j<=m;j++){ pos[i][j]=newnode(read()); pos[i-1][j]->down=pos[i][j]; pos[i][j-1]->right=pos[i][j]; } } for(register int i=1;i<=q;i++){ int dx1=read(),dy1=read(),dx2=read(),dy2=read(),h=read(),w=read(); node *lup1=findpos(dx1-1,dy1),*lup2=findpos(dx2-1,dy2); node *lup3=findpos(dx1,dy1-1),*lup4=findpos(dx2,dy2-1); node *ldown1=findpos(dx1+h-1,dy1),*ldown2=findpos(dx2+h-1,dy2); node *rup1=findpos(dx1,dy1+w-1),*rup2=findpos(dx2,dy2+w-1); for(register int j=1;j<=w;j++){ swap(lup1->down,lup2->down); lup1=lup1->right,lup2=lup2->right; } for(register int j=1;j<=h;j++){ swap(lup3->right,lup4->right); lup3=lup3->down,lup4=lup4->down; } for(register int j=1;j<=w;j++){ swap(ldown1->down,ldown2->down); ldown1=ldown1->right,ldown2=ldown2->right; } for(register int j=1;j<=h;j++){ swap(rup1->right,rup2->right); rup1=rup1->down,rup2=rup2->down; } } for(register int i=1;i<=n;i++){ node *p=pos[i][0]; for(register int j=1;j<=m;j++){ p=p->right; W(p->val);putchar(' '); } putchar('\n'); } }
相关文章推荐
- NOIP模拟 裁剪表格【链表】
- [NOIP2017模拟]裁剪表格
- 【NOIP 模拟赛】钟 模拟+链表
- JZOJ5372. 【NOIP2017提高A组模拟9.17】猫 链表+贪心+堆优化
- NOIP模拟 JZOI5428 查询 【链表】
- 洛谷 P1948 [USACO08JAN]电话线Telephone Lines【NOIP模拟笨笨的电话线】
- 【NOIP2016提高A组模拟9.2】同余
- 【NOIP2015模拟11.3】IOIOI卡片占卜
- 【NOIP2016提高A组模拟9.3】总结
- 线性表(2)--链表模拟
- JZOJ5373. 【NOIP2017提高A组模拟9.17】信仰是为了虚无之人 并查集+启发式合并
- 【JZOJ4924】【NOIP2017提高组模拟12.17】向再见说再见
- [NOIP2014][vijos1913]螺旋矩阵(模拟)
- 【NOIP2012模拟10.25】旅行
- NOIP模拟21题解
- NOIP模拟 NYG的背包 [高山算法]
- NOIP2016年普及组模拟考试(9.3) 3.笨笨连线游戏
- 【啊哈!算法】算法8:巧妙的邻接表(数组实现逆序模拟链表)
- 【JZOJ4761】【NOIP2016提高A组模拟9.7】鼎纹
- UVA 12657/COJ 1329 HN第九届省赛 链表模拟