一、积木大赛
数据:对于70%,n∈[1,1000]
对于 100%,n∈[1,100000]
暴力就是从一个点不断往后刷。贪心的想法,每次都要刷较多的区间,那么注意到高的都比相邻的低的刷的次数多,只要记录A[i]-A[i-1]就行了(A[i]>A[i-1])
二、花匠
数据:对于 70%,n∈[1,1000],hi∈[1,1000]
对于 100%,n∈[1,1e5],hi∈[1,1e6]
注意一点:序列要么全部满足A,要么全部满足B
可以看作为一个LIS的变种(波浪LIS),只是判断的东西不同,因为这个判断分两种,所以多定义一维:dp[0][i]表示以i结尾满足A的个数,dp[1][i]表示以i结尾满足B的个数
优化用Bits就行了,和LIS是一样的,往前找,AB两个条件是相互对立的,只是开始是先大还是先小的区别,开始是大,那么就是B,开始是小,那么就是A, 那么其实两者有很大的共同点,可以两者结合考虑:
dp[0]是下一个dp[1]的值,dp[1]是下一个dp[0]的值
所以更新的时候A用B更新,B用A更新 此时可以重新定义dp:
dp[0][i]表示以i结尾,此时这个数大于上个数
dp[1][i]表示以i结尾,此时这个数小于上个数
小技巧:为了简化Bit,找大的时候要用Mx减一下,这样就等于是在找小的
还要注意一点:bit要从1开始,但是题目里说花的高度从0开始,这样就有问题了,那么先把所有花的高度都加1就行了。
Code:
#include<bits/stdc++.h>
#define M 100005
using namespace std;
int n,A[M],dp[2][M],Mx;
struct BITS{//Bit优化段,和LIS是一样的
int num[1000005];
int Query(int x){
int res=0;
while(x){
if(num[x]>res)res=num[x];
x-=x&-x;
}
return res;
}
void Change(int x,int y){
while(x<=Mx){
if(num[x]<y)num[x]=y;
x+=x&-x;
}
}
}bit[2];//因为分奇偶,用两个来装
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);A[i]++;
if(Mx<A[i])Mx=A[i];
}Mx++;//Mx是用在Bit里的
int ans=0;
for(int i=1;i<=n;i++){//A->B,B->A
dp[0][i]=bit[0].Query(A[i]-1)+1;
dp[1][i]=bit[1].Query(Mx-A[i]-1)+1;//小技巧
bit[0].Change(A[i],dp[1][i]);
bit[1].Change(Mx-A[i],dp[0][i]);//小技巧
if(ans<dp[0][i])ans=dp[0][i];
if(ans<dp[1][i])ans=dp[1][i];
}
printf("%d\n",ans);
return 0;
}
三、华容道
数据:对于60%,n,m∈[1,30],q∈[1,10]
对于100%,n,m∈[1,30],q∈[1,500]
考试的时候只想了BFS,显然只有60分(其实有70,只是起点终点一样没判断,少了10分)
正解也要用到BFS,但只是辅助的,主要是一个想法。
注意到如果一个格子要移动,那么就要让空白格子移到这个格子旁边才行。那么可以先让空白格子移到目标格子旁边四个位子(四个位子就是目标格子上下左右四个,空白格位置不同,最终结果也不同)。然后呢?当然是交换空白和目标喽。然后呢?又要把白色格子移到目标格子旁边(虽然现在就在目标格子旁边,但是显然再交换就回到原样了)。那么就是回到了原来的问题上了。既然是同一个问题,加快其速度就很好办了。
因为n,m只有30,那么就先预处理一下。D[i][j][k][l] 表示i,j坐标上的可以行走的格子从k方向到l方向要移动多少步。这个怎么处理?一个一个算吗?当然,反正数据很小,这样算复杂度也就只有n*m*n*m*4(极限复杂度)。
然后再对应每组数据,先把空白位置交换到目标点旁边,用BFS,因为只用到旁边就行了,复杂度也只是n*m。最后,就只要跑一遍最短路了。
最短路?为什么是最短路?因为已经知道了各种路径了(抽象的理解一下就行了),让交换次数最小就是路径最小。用SPFA或DJ都行(个人喜欢SPFA,方便,而且这道题不卡)
程序之前理一下过程(程序太难看了,看不懂就尬了):
1、先对所有空白点预处理,处理处一个数组D[i][j][k][l] ,为后面的最短路造边。
2、对于每一组数据,先把空格位置走到目标位置附近,BFS走。
3、最短路跑一遍
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,cas,kx,ky,sx,sy,ex,ey;
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
int G[35][35],D[35][35][5][5],dis[35][35];
bool mark[35][35];
struct node{int x,y,step;};
queue<node>Q;
void f1(int Sx,int Sy){//被共用的BFS(预处理和每组数据的处理都要)
memset(dis,63,sizeof dis);
memset(mark,0,sizeof mark);
dis[Sx][Sy]=0;mark[Sx][Sy]=1;
Q.push((node){Sx,Sy,0});
while(!Q.empty()){
node now=Q.front();Q.pop();
for(int i=0;i<4;i++){
int X=now.x+dx[i],Y=now.y+dy[i];
if(X<1||X>n||Y<1||Y>m||mark[X][Y]||!G[X][Y])continue;
mark[X][Y]=1;dis[X][Y]=now.step+1;
node nxt=now;nxt.x=X;nxt.y=Y;nxt.step++;
Q.push(nxt);
}
}
}
void solve(){//预处理
memset(D,63,sizeof D);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(G[i][j]){//仅对所有可以移动的位置
G[i][j]=0;//移动不能穿过自己
for(int k=0;k<4;k++){
if(i+dx[k]<1||i+dx[k]>n||j+dy[k]<1||j+dy[k]>m||!G[i+dx[k]][j+dy[k]])continue;
//超出边界或者是墙
f1(i+dx[k],j+dy[k]);
for(int l=0;l<4;l++)D[i][j][k][l]=dis[i+dx[l]][j+dy[l]];
}
G[i][j]=1;
}
}
struct node1{int x,y,k;};
queue<node1>q;
int dis1[35][35][5],Q1[35][35][5];
void Spfa(){//最短路
memset(dis1,63,sizeof dis1);
memset(Q1,0,sizeof Q1);
while(!q.empty())q.pop();
for(int i=0;i<4;i++){//因为有四个情况(上下左右),把符合的都放到队列里
int X=sx+dx[i],Y=sy+dy[i];
if(X>0&&X<=n&&Y>0&&Y<=m&&G[X][Y]&&dis[X][Y]<1e9){
//超边界和走不到
q.push((node1){sx,sy,i});
Q1[sx][sy][i]=1;
dis1[sx][sy][i]=dis[X][Y];
}
}
while(!q.empty()){
node1 now=q.front();q.pop();Q1[now.x][now.y][now.k]=0;
//这一块是空格和目标位置交换位置
int X1=now.x+dx[now.k],Y1=now.y+dy[now.k];
if(dis1[X1][Y1][(now.k+2)%4]>dis1[now.x][now.y][now.k]+1){
dis1[X1][Y1][(now.k+2)%4]=dis1[now.x][now.y][now.k]+1;
if(!Q1[X1][Y1][(now.k+2)%4])Q1[X1][Y1][(now.k+2)%4]=1,q.push((node1){X1,Y1,(now.k+2)%4});
}
//这一个是空格换到目标格子的旁边(注意判断方向,避免空格位换到空格位)
for(int i=0;i<4;i++){
int X=now.x+dx[i],Y=now.y+dy[i];
if(X<0||X>n||Y<0||Y>m||!G[X][Y]||D[now.x][now.y][now.k][i]>1e9||i==now.k)continue;
if(dis1[now.x][now.y][i]>dis1[now.x][now.y][now.k]+D[now.x][now.y][now.k][i]){
dis1[now.x][now.y][i]=dis1[now.x][now.y][now.k]+D[now.x][now.y][now.k][i];
if(!Q1[now.x][now.y][i])Q1[now.x][now.y][i]=1,q.push((node1){now.x,now.y,i});
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&cas);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)scanf("%d",&G[i][j]);
solve();
while(cas--){
scanf("%d%d%d%d%d%d",&kx,&ky,&sx,&sy,&ex,&ey);
if(sx==ex&&sy==ey){printf("0\n");continue;}
G[sx][sy]=0;//同样空格不能穿过目标格
f1(kx,ky);
G[sx][sy]=1;
Spfa();
int ans=1e9;
for(int i=0;i<4;i++)ans=min(ans,dis1[ex][ey][i]);//如果是1e9那就不能到达
if(ans==1e9)printf("-1\n");else printf("%d\n",ans);
}
return 0;
}
其实BFS是很好打的,主要是想法不容易想到。