2013

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;
}
标题基于SpringBoot+Vue的社区便民服务平台研究AI更换标题第1章引言介绍社区便民服务平台的研究背景、意义,以及基于SpringBoot+Vue技术的研究现状和创新点。1.1研究背景与意义分析社区便民服务的重要性,以及SpringBoot+Vue技术在平台建设中的优势。1.2国内外研究现状概述国内外在社区便民服务平台方面的发展现状。1.3研究方法与创新点阐述本文采用的研究方法和在SpringBoot+Vue技术应用上的创新之处。第2章相关理论介绍SpringBoot和Vue的相关理论基础,以及它们在社区便民服务平台中的应用。2.1SpringBoot技术概述解释SpringBoot的基本概念、特点及其在便民服务平台中的应用价值。2.2Vue技术概述阐述Vue的核心思想、技术特性及其在前端界面开发中的优势。2.3SpringBoot与Vue的整合应用探讨SpringBoot与Vue如何有效整合,以提升社区便民服务平台的性能。第3章平台需求分析与设计分析社区便民服务平台的需求,并基于SpringBoot+Vue技术进行平台设计。3.1需求分析明确平台需满足的功能需求和性能需求。3.2架构设计设计平台的整体架构,包括前后端分离、模块化设计等思想。3.3数据库设计根据平台需求设计合理的数据库结构,包括数据表、字段等。第4章平台实现与关键技术详细阐述基于SpringBoot+Vue的社区便民服务平台的实现过程及关键技术。4.1后端服务实现使用SpringBoot实现后端服务,包括用户管理、服务管理等核心功能。4.2前端界面实现采用Vue技术实现前端界面,提供友好的用户交互体验。4.3前后端交互技术探讨前后端数据交互的方式,如RESTful API、WebSocket等。第5章平台测试与优化对实现的社区便民服务平台进行全面测试,并针对问题进行优化。5.1测试环境与工具介绍测试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值