【DP】CodeForces612F Simba on the Circle

2023-10-20 16:40
文章标签 dp circle simba codeforces612f

本文主要是介绍【DP】CodeForces612F Simba on the Circle,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意:

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


分析:

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

第一问:

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

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

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

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

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

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

1、

这里写图片描述

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

2、

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

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

这部分可以等价地表示为: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)} 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求方案的题一样,这道题也需要记录转移路径。

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

对于第1类:

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

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

对于第2类:

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

对于第3类:

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

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

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

或从 Gj G j 走到 Slasp S l a s p ,以递增的顺序走完 S S 中的点回到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;elsereturn 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]");}elseval=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;elsereturn -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]);elsePF("%d\n",print[i]);}
}

这篇关于【DP】CodeForces612F Simba on the Circle的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/248347

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int