2013
来源:互联网 发布:乐乎青年公寓朝阳 编辑:程序博客网 时间:2024/04/30 06:04
2013D2T1
积木赛
Task
n个初始为0的数,求最少的操作次数变成目标数组。
一次操作:选择区间[ l , r ]内所有数+1
n<=1e5,0<=hi<=1e4
Solution
贪心:
为使操作次数最少,每次选的区间应尽可能的大。因此每次贪心选一段最长都是正数的区间,区间内数-1.最值可以用堆维护。
如果当前选出的区间操作完后不含0,那么下一次还是会选择这个区间,为减少操作次数,
如果一次性减去这个区间的最小值,就一定会含0,区间分成2个子区间。
用堆写还是太麻烦了,不如dfs,每次找到这个区间内的最小值,分成2个子区间。
const int M=1e5+5;int A[M],n;struct node{ int l,r,x; bool operator<(const node &t)const{ r-l>t.r-t.l; }};priority_queue<node>Q;inline void input(){ int i; rd(n); rep(i,1,n)rd(A[i]);}inline void add(int L,int R,int x){ int i,k,mi,l,r; rep(i,L,R)A[i]-=x; rep(i,L,R){ if(A[i-1]==0&&A[i]>0){ l=i; mi=A[i]; } MIN(mi,A[i]); if(A[i]>0&&A[i+1]==0){ Q.push((node){l,i,mi}); } }}inline void solve(){ int i,j,k,ans=0,l,r; node res; while(!Q.empty()){ res=Q.top(); Q.pop(); ans+=res.x; add(res.l,res.r,res.x); } printf("%d\n",ans);}int main(){ input(); add(1,n,0); solve(); return 0;}
LIN452大神提供了一种O(n)的方法。
刷漆:
区间加除了线段树以外,还可以用刷漆法处理,左端点+1,右端点+1的位置-1。
对于i点而言,权值从0变成hi,说明[1,i]操作前缀和是hi。对于i-1点而言,操作的前缀和是h[i-1],说明对i点的操作数是h[i]-h[i-1],是以i为操作区间左端点的最少次数。
const int M=1e5+5;int A[M];int n;int main(){ int i,j,k,ans=0; rd(n); rep(i,1,n)rd(A[i]); rep(i,1,n)ans+=max(0,A[i]-A[i-1]); printf("%d\n",ans);}
2013D2T2
花匠
Task
在n个数去除一些数,使剩余的数满足条件A或条件B,求最多剩余数?
条件A:对所有的i,g[2i]>g[2i-1],g[2i]>g[2i+1]
条件B:对所有的i,g[2i]小于g[2i-1],g[2i]小于g[2i+1]
n<=1e5,hi<=1e6
Solution
考察LIS的dp思想。
发现最后满足的数是波浪锯齿状,且2个为1组。每个数的选择:不选,组内第一个数,组内第二个数。每一个数都是接在符合条件的另一个数后面。
如果定义dp[0][i],i作为组第一个,dp[1][i]i作为组第二个。
条件A:
Dp[0][i]=max(dp[1][j])+1 j小于i&&A[j]>A[i]
Dp[1][i]=max(dp[0][i])+1 j小于i&&A[j]小于A[i]
同样的条件B的式子也可以列出。
普通的转移是O(n^2)
但是我们可以借助线段树优化,因为是维护前缀后缀的最值,也可以用树状数组来代替,代码更短,效率更高。
但是比赛时以为后缀的下标搞错了,一个测试点WA了。
X对应的后缀下标是tot-x+1.因为[1,tot] 对应[tot,1]
const int M=1e5+5;int A[M],B[M];int n,ans,tot;struct P70{ int dp[2][1005]; inline void solve(){ int i,j,k,ans=0; rep(i,1,n)rd(A[i]); A[0]=1e9; rep(i,1,n){//条件A rep(j,0,i-1){ if(A[j]>A[i])MAX(dp[0][i],dp[1][j]+1); if(A[j]<A[i])MAX(dp[1][i],dp[0][j]); } } rep(i,1,n){ MAX(ans,dp[0][i]*2-1); MAX(ans,dp[1][i]*2); } memset(dp,0,sizeof(dp)); A[0]=-1; rep(i,1,n){ rep(j,0,i-1){ if(A[j]<A[i])MAX(dp[0][i],dp[1][j]+1); if(A[j]>A[i])MAX(dp[1][i],dp[0][j]); } } rep(i,1,n){ MAX(ans,dp[0][i]*2-1); MAX(ans,dp[1][i]*2); } printf("%d\n",ans); }}P70;struct Tree{ int bit[M]; inline void clear(){memset(bit,0,sizeof(bit));} inline void modify(int i,int x){ while(i<=tot){ MAX(bit[i],x); i+=lowbit(i); } } inline int query(int i){ int mx=0; while(i>0){ MAX(mx,bit[i]); i-=lowbit(i); } return mx; }}T[2];struct P100{ inline void init(){ int i,j,k; rep(i,1,n)rd(A[i]),B[i-1]=A[i]; sort(B,B+n); tot=unique(B,B+n)-B; rep(i,1,n)//离散 A[i]=lower_bound(B,B+tot,A[i])-B+1; } inline void solve(){ int i,j,k,ans=0,a,b; init(); T[0].clear();T[1].clear(); rep(i,1,n){ a=T[1].query(tot-A[i])+1; b=T[0].query(A[i]-1)+1; T[0].modify(A[i],a); T[1].modify(tot-A[i]+1,b); MAX(ans,max(a,b)); } T[0].clear();T[1].clear(); rep(i,1,n){ a=T[1].query(A[i]-1)+1; b=T[0].query(tot-A[i])+1; T[0].modify(tot-A[i]+1,a); T[1].modify(A[i],b); MAX(ans,max(a,b)); } printf("%d\n",ans); }}P100;int main(){ rd(n); if(n<=1000){P70.solve();return 0;} P100.solve(); return 0;}
2013D2T3
华容道
Task
N*m棋盘上有n*m-1个棋子,1个空白格,棋子有若干个不能移动,剩余的可以移动,空白格只能和四周可以移动的格子交换位置。
Q个询问,除了不能移动的棋子外,空白格,其余棋子,初始目标位置都可能改变,求某棋子从初始位置到目标位置的最小步数。
60%数据,n,m<=30,q<=10
100% 数据,n,m<=30,q<=500
Solution
60分bfs
类似judge1205推箱子,从初始位置移动到目标位置的最小步数,唯一不同的是该题是多次询问。bfs可以求解最小步数的问题。
因为我们只关心空白格子和初始特别格子的位置,因此在bfs时只用记录这2个的坐标。
搜索时,注意判重,不合法的情况。
值得一提的是,Y同学用dfs?bfs?做没有判重,电脑死机了。唉╮(╯▽╰)╭
100分 最短路
题目中有一句话:要将指定块移入目标位置,必须先将空白块移入目标位置。给我带来了很大的提示。
更广泛的说,如果x要达到某个位置,必须先将空白格移到该位置,再交换,且该位置一定在x的四周。
60分是搜索空白格的路径,但100分是搜索x的路径,相比60分而言,同样是bfs,但是主体不同。可以一步代替多步,从无方向到有方向。
如果我已知x位置和空白格位置(一定在x的四周)那么x只能和空白格交换,因此可以得知x的新位置,空白格也必须移动到x的四周。
因此可以预处理点(x,y)在不经过中心点的情况下,到达中心点四周的最短路。
const int M=30;int rx[]={0,1,0,-1},ry[]={1,0,-1,0};int dis[M][M][4][M][M],val[M][M][4];bool A[M][M],vis[M][M],use[M][M][4];int n,m,q;struct qu{ int x,y;}Qu[905];struct node{ int x,y,d,v; bool operator<(const node &t)const{ return v>t.v; }};priority_queue<node>Q;inline void input(){ int i,j,k; rd(n);rd(m);rd(q); rep(i,0,n-1) rep(j,0,m-1)rd(A[i][j]);}inline bool check(int x,int y){ return x>=0&&x<n&&y>=0&&y<m&&A[x][y]==1;}inline void bfs(int a,int b,int p){ int i,j,k,x,y,l,r,nx,ny; qu res; x=a+rx[p],y=b+ry[p]; memset(vis,0,sizeof(vis)); vis[a][b]=vis[x][y]=1; l=r=dis[a][b][p][x][y]=0; Qu[r++]=(qu){x,y}; while(l<r){ res=Qu[l++]; rep(i,0,3){ nx=res.x+rx[i],ny=res.y+ry[i]; if(!check(nx,ny)||vis[nx][ny])continue; vis[nx][ny]=1; dis[a][b][p][nx][ny]=dis[a][b][p][res.x][res.y]+1; Qu[r++]=(qu){nx,ny}; } }}inline void init(){ int i,j,k,ii,jj; memset(dis,-1,sizeof(dis)); rep(i,0,n-1) rep(j,0,m-1){ if(A[i][j]==0)continue; rep(k,0,3) if(check(i+rx[k],j+ry[k])){ bfs(i,j,k); } }}inline int dijkstra(int tx,int ty){ int i,j,k,x,y; node res; while(!Q.empty()){ res=Q.top();Q.pop(); if(use[res.x][res.y][res.d])continue; if(res.x==tx&&res.y==ty)return val[res.x][res.y][res.d]; use[res.x][res.y][res.d]=1; x=res.x+rx[res.d],y=res.y+ry[res.d]; rep(i,0,3){ k=dis[x][y][i][res.x][res.y]; if(k>=0){ if(res.v+k+1<val[x][y][i])val[x][y][i]=res.v+k+1; Q.push((node){x,y,i,res.v+k+1}); } } } return -1;}inline void solve(){ int i,j,k,wx,wy,bx,by,tx,ty; while(q--){ rd(wx);rd(wy);rd(bx);rd(by);rd(tx);rd(ty); wx--,wy--,bx--,by--,tx--,ty--; if(bx==tx&&by==ty){puts("0");continue;} memset(use,0,sizeof(use)); memset(val,0x3f,sizeof(val)); while(!Q.empty())Q.pop(); rep(i,0,3){ k=dis[bx][by][i][wx][wy]; if(~k){ val[bx][by][i]=k; Q.push((node){bx,by,i,k}); } } printf("%d\n",dijkstra(tx,ty)); }}int main(){ input(); init(); solve(); return 0;}
- 2013
- 2013
- 2013!
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013
- 2013: Sum
- sicily 2013
- hdu 2013
- bug生命周期的几个状态
- [删边最短路 并查集||线段树] BZOJ 2725 [Violet 6]故乡的梦 & 4400 tjoi2012 桥
- 微信小程序实战(1)——微信公众平台小程序开放公测
- 浅谈区间问题
- 洛谷1111修复公路
- 2013
- 2016-11-04 临界知识修订建议
- Maximum Subarray II
- Java程序设计——下拉选择框
- Android java代码 设置button控件大小
- kafka install
- UVA 11488 Hyper Prefixs Sets(字典树Trie)
- Wikioi 1152 细胞分裂 (快速幂)
- 捕获处理异常