【noip2013】【提高组】【Day2】【解题报告】

这篇博客详细介绍了NOIP2013提高组第二天的三道解题报告。第一题是积木大赛,通过累加高度差求解,时间复杂度为O(n);第二题使用O(n^2)的动态规划解决,通过树状数组优化达到O(n*logn);第三题针对100%数据预处理并使用广搜求解,复杂度为O(n^4+k*n^2)。每道题都提供了相应的代码链接供读者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T1:积木大赛

题目链接:http://codevs.cn/problem/3288/

题解:可以发现如果后面一个格子比前面一个格子高,那么高出来的那块一定要单独处理。

         所以只要把后一个积木和前一个积木的高度差累加进答案再加上第一块积木的高度即可。

         复杂度O(n);

代码:

#include<iostream>
#include<cstdio>
#define N 100010
using namespace std;
int h[N],ans,minn(99999999),s[N],n;
int main(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++) {scanf("%d",&h[i]);}
  for (int i=1;i<=n;i++){
    if (h[i]>h[i-1]) ans+=h[i]-h[i-1];
  }
  cout<<ans<<endl; 	
}

T2:花匠

题目链接:http://codevs.cn/problem/3289/

题解:首先O(n^2)的dp还是很简单的

         设f[i][1]表示到i结束,最后两盆花之间的趋势是上升的最大长度。

             f[i][0]表示到i结束,最后两盆花之间的趋势是下降的最大长度。

         显然

               f[i][1]=max(f[j][0]+1,f[i][1])(h[j]<h[i]);

               f[i][0]=max(f[j][1]+1,f[i][0])(h[j]>h[i]);

        因为是取最大值,所以我们用树状数组优化一下就可以通过了。

        复杂度O(n*logn)

代码:

#include<iostream>
#include<cstdio>
#define N 100010
using namespace std;
int f[N][2],h[N],ans,n,c1[N*10],c2[N*10],maxx;
void add(int c[],int x,int v){for (int i=x;i<=maxx+1;i+=(i&(-i))) c[i]=max(c[i],v);}
int query(int c[],int x){
  int ans(0);for (int i=x;i;i-=(i&(-i))) ans=max(ans,c[i]);
  return ans;
}
int main(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++) {scanf("%d",&h[i]);maxx=max(maxx,++h[i]);} 
  maxx++;add(c1,h[1],1);add(c2,maxx-h[1],1);
  for (int i=2;i<=n;i++){
	  f[i][1]=query(c1,h[i]-1)+1;f[i][0]=query(c2,maxx-h[i]-1)+1;
          add(c2,maxx-h[i],f[i][1]);add(c1,h[i],f[i][0]);
	  ans=max(ans,max(f[i][1],f[i][0]));
  }
 cout<<ans<<endl;
} 

T3:华容道

题目链接:http://codevs.cn/problem/3290/

题解:这个题还是比较麻烦的。

            首先暴力bfs就可以通过前60%的数据.

            对于100%的数据。

            我们预处理一下p[x][y][k][h]表示当前格子在(x,y),空白格子在和它相邻的k方向,当前格子要往h方向移动1格的最小步数。

            这个东西显然可以广搜.复杂度O(n^4);

            然后我们把[x][y][k]作为状态,首先把空白格子bfs到起始格子的上下左右四个方向作为初始状态。

            考虑跑最短路。状态[x][y][k]显然可以更新到[x+dx[i]][y+dy[i]][k'],k'是i的反向。

            设目标格子为(tx,ty),

            最后答案在[tx][ty][0],[tx][ty][1],[tx][ty][2],[tx][ty][3]中取最小值即可.

            最终复杂度O(n^4+k*n^2)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 707406378
using namespace std;
int n,m,q,ex,ey,sx,sy,tx,ty,map[50][50],p[50][50][5][5];
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0},l[1000000][4],dis[50][50],d[50][50][4];
bool f[50][50][5];
int bfs(int x,int y,int xx,int yy){
  if (!map[x][y]||!map[xx][yy]) return INF;
   memset(dis,127/3,sizeof(dis));
  int h=0,t=1;l[t][1]=x;l[t][2]=y;dis[x][y]=0;
  while (h<t){
    int a=l[++h][1],b=l[h][2];
	if (a==xx&&b==yy) return dis[a][b];
	for (int i=0;i<4;i++){
	   int aa=a+dx[i],bb=b+dy[i];
	   if (aa>0&&aa<=n&&bb>0&&bb<=m&&dis[aa][bb]==INF&&map[aa][bb]){
	      dis[aa][bb]=dis[a][b]+1;l[++t][1]=aa;l[t][2]=bb;
	   }
	}	
  }return INF;
}
int spfa(){
  int h(0),t(0),ans(INF);memset(f,0,sizeof(f));
  for (int i=0;i<=4;i++)if (d[sx][sy][i]!=INF){
   l[++t][1]=sx;l[t][2]=sy;l[t][3]=i;
   f[sx][sy][i]=true;
   }
  while (h<t){
    int x=l[++h][1],y=l[h][2],k=l[h][3];f[x][y][k]=false;
	for (int i=0;i<4;i++)
	  if (d[x+dx[i]][y+dy[i]][i^1]>d[x][y][k]+p[x][y][k][i]+1&&map[x+dx[i]][y+dy[i]]){
	    d[x+dx[i]][y+dy[i]][i^1]=d[x][y][k]+p[x][y][k][i]+1;
	    if (!f[x+dx[i]][y+dy[i]][i^1]){
	       l[++t][1]=x+dx[i];l[t][2]=y+dy[i];l[t][3]=i^1;
		   f[x+dx[i]][y+dy[i]][i^1]=true; 
	    }
	  }
   }
  for (int i=0;i<4;i++) ans=min(ans,d[tx][ty][i]);
  if (ans==INF) return -1;else return ans;
}
void cal(int ex,int ey,int sx,int sy,int tx,int ty){
  if (!map[ex][ey]||!map[sx][sy]||!map[tx][ty]) {printf("-1\n");return;};
  if (sx==tx&&sy==ty){printf("0\n");return;}
  memset(d,127/3,sizeof(d));map[sx][sy]=0;
  for (int i=0;i<4;i++) d[sx][sy][i]=bfs(ex,ey,sx+dx[i],sy+dy[i]);
  map[sx][sy]=1;printf("%d\n",spfa());
}
void pre(){
  for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++){
      int t=map[i][j];map[i][j]=0;
      for (int k=0;k<4;k++)
       for (int h=0;h<4;h++)
          p[i][j][k][h]=bfs(i+dx[k],j+dy[k],i+dx[h],j+dy[h]);
	  map[i][j]=t;
     }
}
int main(){
  scanf("%d%d%d",&n,&m,&q);
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
     scanf("%d",&map[i][j]);
  pre();
  for (int i=1;i<=q;i++){
   scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);cal(ex,ey,sx,sy,tx,ty);
   }
} 



        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值