【DP】CodeForces612F Simba on the Circle

题意:

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


分析:

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

第一问:

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

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

所以 d p i = m i n { d p G j + G j S i i }
我们可以O(n)地枚举 G j ,然后用O(1)的复杂度求出后面那一坨。。。(总复杂度 O ( n 2 )

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

首先要引入2个辅助函数
l e n ( u , v ) 表示从u到v的最小步数。
l e n ( u , v , k ) 表示从u出发,不经过k到达v的步数。
p r e p 表示在S中i左边的第一个位置 ( p r e p = ( i 1 + | s | ) m o d | s | )
l a s p 表示在S中i右边的第一个位置 ( l a s p = ( i + 1 ) m o d | s | )
由于我们定义的 d p i 的特殊要求(在当前层中, i 位置只经过一次)

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

1、

这里写图片描述

这种情况(好吧其实是2种)满足 p r e p = l a s p
代价可以表示为: l e n ( G j , S p r e p ) + l e n ( S p r e p , S i )

2、

这里写图片描述
这种情况满足 ( l > S p r e p , r > S l a s p , x > G j )
$$
说白了,就是 G j 被包在 S p r e p , S i S i , S l a s p 之间。

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

这部分可以等价地表示为: n l e n ( G j , S i , S p r e p ) ( = n l e n ( G j , S i , S l a s p ) )

3、

这里写图片描述

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

取个min就可以了

所以表示为:

l e n ( G j , S p r e p , S i ) + l e n ( G j , S l a s p , S i ) +   m i n { l e n ( G j , S p r e p , S i ) + l e n ( S l a s p , S i ) , l e n ( G j , S l a s p , S i ) + l e n ( S p r e p , S i ) }

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

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

第二问

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

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

对于第1类:

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

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

对于第2类:

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

对于第3类:

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

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

这里我们可以直接从 G j 走到 S p r e p ,再以递减的顺序走完 S 中的点回到 S i

或从 G j 走到 S l a s p ,以递增的顺序走完 S 中的点回到 S i

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

#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]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_34454069/article/details/81156632
今日推荐