【DP】CodeForces612F Simba on the Circle

本文介绍了一种在环状序列中高效遍历的方法,通过动态规划优化遍历步骤,达到最小化总步数的目的。文章详细阐述了算法设计思路、状态转移方程,并提供了具体的实现代码。

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

题意:

给出一个环状的序列,每个位置有一个值aiai,初始位置为s,现在要求从小到大依次遍历每个点。要求总步数尽可能小。
序列长度N2000N≤2000


分析:

这是一道代码题。。。。
所谓代码题,就是思路异常简单,但实现起来细节暴多的题。。。。

第一问:

其实大致方法很简单,定义dp[i]dp[i]表示将所有ajaiaj≤ai的位置都遍历后,停在aiai位置的最小步数(注:必须满足在遍历aj=aiaj=ai这一层时,ii位置只能经过一次)。

转移就非常暴力了。
我们对ai相同的在同一次处理,定义为SS
定理序列G表示aSiaSi为严格小于aiai的最大的数组成的序列(说白了就是上一次处理的那些位置)

所以dpi=min{dpGj+GjSii}dpi=min{dpGj+从Gj出发遍历所有Si中的点最终到达i的最小步数}
我们可以O(n)地枚举GjGj,然后用O(1)的复杂度求出后面那一坨。。。(总复杂度O(n2)O(n2)

所以,恶心的就是如何用O(1)的复杂度求出后面那一坨。。。。

首先要引入2个辅助函数
len(u,v)len(u,v)表示从u到v的最小步数。
len(u,v,k)len(u,v,k)表示从u出发,不经过k到达v的步数。
prepprep表示在S中i左边的第一个位置(prep=(i1+|s|)mod|s|)(prep=(i−1+|s|)mod|s|)
lasplasp表示在S中i右边的第一个位置(lasp=(i+1)mod|s|)(lasp=(i+1)mod|s|)
由于我们定义的dpidpi的特殊要求(在当前层中,ii位置只经过一次)

所以我们可以分为这么3种情况:

1、

这里写图片描述

这种情况(好吧其实是2种)满足prep=lasp
代价可以表示为:len(Gj,Sprep)+len(Sprep,Si)len(Gj,Sprep)+len(Sprep,Si)

2、

这里写图片描述
这种情况满足(l>Sprep,r>Slasp,x>Gj)(l−>Sprep,r−>Slasp,x−>Gj)
$$
说白了,就是GjGj被包在Sprep,SiSprep,SiSi,SlaspSi,Slasp之间。

这种情况下我们肯定要从GjGj出发,绕外面所有SS中的点一圈,再从另一侧回来

这部分可以等价地表示为:nlen(Gj,Si,Sprep)(=nlen(Gj,Si,Slasp))

3、

这里写图片描述

这部分就相对常规一些,公式也相对复杂一些:
这里写图片描述
我们可以走基佬紫和原谅绿两种颜色表示的路径。

取个min就可以了

所以表示为:

len(Gj,Sprep,Si)+len(Gj,Slasp,Si)+ min{len(Gj,Sprep,Si)+len(Slasp,Si),len(Gj,Slasp,Si)+len(Sprep,Si)}len(Gj,Sprep,Si)+len(Gj,Slasp,Si)+ min{len(Gj,Sprep,Si)+len(Slasp,Si),len(Gj,Slasp,Si)+len(Sprep,Si)}

min里面前者表示基佬紫走法
后者表示原谅绿走法

然后。。。我们就可以得到第一问的答案了。。

第二问

和所有DP求方案的题一样,这道题也需要记录转移路径。

这里还要借助一个辅助函数len1(u,v)len1(u,v)表示从u走到v的小步数(含正负)
然后还是分为上面3类讨论:

对于第1类:

走法无非是这里写图片描述

应该很简单我就直接粘代码了。

对于第2类:

上面已经把走法解释过了,所以我还是直接上代码吧:
这里写图片描述

对于第3类:

这里的具体走法稍微有点巧妙。

首先还是判断一下哪条路代价最小,然后走小的那一边。

这里我们可以直接从GjGj走到SprepSprep,再以递减的顺序走完SS中的点回到Si

或从GjGj走到SlaspSlasp,以递增的顺序走完SS中的点回到Si

实在还有不懂的就去看代码吧。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 2010
#define INF 0x3f3f3f3f
using namespace std;
int n,s;
int dp[MAXN],com[MAXN],cnt;
vector<int> las[MAXN],now;
pair<int,int> a[MAXN];
int len(int x,int y){
    if(x>y)
        swap(x,y);
    return min(y-x,x+n-y);
}
int len(int x,int y,int blo){
    if(x>y)
        swap(x,y);
    if(blo==x||blo==y)
        return len(x,y);
    if(x<blo&&blo<y)
        return x+n-y;
    if(blo<x||blo>y)
        return y-x;
}
bool checkin(int x,int l,int r){
    if(l<r)
        return x>=l&&x<=r;
    else
        return x<=r||x>=l;
}
void solve(){
    int m=now.size();
    for(int i=0;i<m;i++)
        for(int j=0;j<las[cnt].size();j++){
            int prep=now[(i-1+m)%m];
            int lasp=now[(i+1+m)%m];
            int u=now[i];
            int v=las[cnt][j];
            int val=0;
            if(prep==lasp){
                val=len(v,prep)+len(prep,u);
                //PF("[1]");
            }
            else if(checkin(v,prep,lasp)){
                val=n-len(u,v,prep);
                //PF("[2]");
            }
            else
                val=len(v,prep,u)+len(v,lasp,u)+min(len(v,lasp,u)+len(prep,u),len(v,prep,u)+len(lasp,u));
            //PF("{%d %d}\n",u,val);
            if(cnt!=0)
                val+=dp[las[cnt][j]];
            if(val<dp[now[i]]){
                dp[now[i]]=val;
                com[now[i]]=j;  
            }
        }
    cnt++;
    for(int i=0;i<m;i++)
        las[cnt].push_back(now[i]);
    now.clear();
}
vector<int> print;
int len1(int x,int y){
    //PF("{%d %d}\n",x,y);
    int lenx=len(x,y);
    if((x+lenx-1)%n+1==y)
        return lenx;
    else
        return -lenx;
}
void get_ans(int x,int tim){
    if(tim!=1)
        get_ans(com[las[tim][x]],tim-1);
    int m=las[tim].size();
    int prep=las[tim][(x-1+m)%m];
    int lasp=las[tim][(x+1)%m];
    int u=las[tim][x];
    int v=las[tim-1][com[u]];
    int val=0;
    if(prep==lasp){
        print.push_back(len1(v,prep));
        if(prep!=u)
            print.push_back(len1(prep,u));
    }
    else if(checkin(v,prep,lasp)){
        if(checkin(v,prep,u)){
            print.push_back(len1(v,prep));
            for(int j=(x-1+m)%m;j!=x;j=(j-1+m)%m)
                print.push_back(len1(las[tim][j],las[tim][(j-1+m)%m]));
        }
        else{
            print.push_back(len1(v,lasp));
            for(int j=(x+1)%m;j!=x;j=(j+1)%m)
                print.push_back(len1(las[tim][j],las[tim][(j+1)%m]));
        }
    }
    else{
        if(len(v,lasp,u)+len(prep,u)<len(v,prep,u)+len(lasp,u)){
            print.push_back(len1(v,lasp));
            for(int j=(x+1)%m;j!=x;j=(j+1)%m)
                print.push_back(len1(las[tim][j],las[tim][(j+1)%m]));
        }
        else{
            print.push_back(len1(v,prep));
            for(int j=(x-1+m)%m;j!=x;j=(j-1+m)%m)
                print.push_back(len1(las[tim][j],las[tim][(j-1+m)%m]));
        }
    }
    //PF("{%d %d %d}\n",u,tim,print.size());
}
int main(){
    memset(dp,INF,sizeof dp);
    SF("%d%d",&n,&s);
    for(int i=1;i<=n;i++){
        SF("%d",&a[i].first);
        a[i].second=i;
    }
    sort(a+1,a+1+n);
    las[0].push_back(s);
    now.push_back(a[1].second);
    for(int i=2;i<=n;i++){
        if(a[i].first!=a[i-1].first){
            solve();
        }
        now.push_back(a[i].second);
    }
    solve();
    /*for(int i=0;i<=cnt;i++){
        for(int j=0;j<las[i].size();j++)
            PF("{%d %d} ",las[i][j],dp[las[i][j]]);
        PF("\n");
    }*/
    int minid=0,minv=INF;
    for(int i=0;i<las[cnt].size();i++)
        if(dp[las[cnt][i]]<minv){
            minid=i;
            minv=dp[las[cnt][minid]];   
        }
    PF("%d\n",minv);
    get_ans(minid,cnt);
    for(int i=0;i<print.size();i++){
        if(print[i]>=0)
            PF("+%d\n",print[i]);
        else
            PF("%d\n",print[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值