SRM627 Div2 1000 DP

【题意】:
给出一个数列,可以进行的操作为最多能取K个互相不重叠的区间并将其反转,问经过操作以后,用冒号排序法排序数组所能达到的数的交

换次数的最小值。
例如:一个数列{7,2,2,13,5,5,2}最多可以取2个互相不重叠的区间,那么有[0,2],[3,6],反转后的数组为{2,2,7,2,5,5,13},用冒号排

序法排序时所需要的交换次数为3。
【知识点】:DP
【题解1】:
记忆化搜索DP(递归DP)
虽然我知道记忆化搜索这东西本身,但因为智商拙计经常用不上去,我觉得自己真的对编码本身还有待提高,下面通过几个步骤解析:
1、变量说明:
dp1[pos][rem]:代表从第pos到末尾这段数组内取了rem个不重叠子数组反转后用冒号排序法排序时所需要的最小交换次数。
dp2[pos][i]:区间[pos,i]经过反转后该区间内的数在经过对整个区间的冒号排序后所需要的最小交换次数。
dp1[pos][rem]、dp2[pos][i]初始均为-1,当这两个值不为-1时表示已经访问过,略过计算直接使用,其中dp1[pos][rem]所实现的即为记

忆化搜索。
2、状态转移方程:
dp1[pos][rem] = min(dp1[pos][rem], dfs(i + 1, rem - ((i == pos) ? 0 : 1)) + cur);
cur:代表[i,pos]的花费。当i==pos时不存在反转,故rem不变。
3、逻辑结构解析:
1)怎么实现连续区间不反转的?连续几次dfs都满足i == pos。
2)连续区间不反转且K的次数已用尽,剪枝:if(rem == 0 && i > pos) break;。
3)dfs终点:当pos超过数组尾端时,返回0。
【代码1】:

 1 //type code which was others
 2
 3 #include <vector>
 4 #include <cstring>
 5 #define clr(abc,z) memset(abc,z,sizeof(abc))
 6 using namespace std;
 7 const int inf = 1e9;
 8
 9 class BubbleSortWithReversals{
10 public:
11     vector<int> data;
12     int dp1[55][55], dp2[55][55];
13     int dfs(int pos, int rem){
14         if(pos == data.size()) return 0;
15         if(dp1[pos][rem] != -1) return dp1[pos][rem];
16         dp1[pos][rem] = inf;
17         for(int i = pos; i < data.size(); i++){
18             if(rem == 0 && i > pos) break;
19             int cur = 0;
20             if(dp2[pos][i] != -1)
21                 cur = dp2[pos][i];
22             else{
23                 for(int j = pos; j <= i; j++)
24                 for(int k = j + 1; k <= i; k++)
25                     if(data[j] < data[k]) cur++;
26                 for(int j = pos; j <= i; j++)
27                 for(int k = i + 1; k < data.size(); k++)
28                     if(data[j] > data[k]) cur++;
29                 dp2[pos][i] = cur;
30             }
31             dp1[pos][rem] = min(dp1[pos][rem], dfs(i + 1, rem - ((i == pos) ? 0 : 1)) + cur);
32         }
33         return dp1[pos][rem];
34     }
35     int getMinSwaps(vector <int> A, int K){
36         clr(dp1, -1); clr(dp2, -1); data = A;
37         return dfs(0, K);
38     }
39 };

【题解2】:

递推DP
1、变量说明:
f1[i][j]:[i,j]区间内的数不经反转在整个区间内进行冒号排序所需花费的交换次数。
f2[i][j]:[i,j]区间内的数经反转在整个区间内进行冒号排序所需花费的交换次数。
dp[i][j]:反转不大于j个不相交区间的前提下,[1, i]区间元素在整个区间内进行冒号排序所需花费的最小交换次数。
2、状态转移方程:
dp[i][j] = min(dp[i][j], dp[k][j] + f1[k + 1][i]);不反转情况
if(j) dp[i][j] = min(dp[i][j], dp[k][j - 1] + f2[k + 1][i]);可反转情况
3、逻辑结构解析:
1)clr(dp, 10); 初始值赋予最大值。
2)dp[0][0] = 0;状态转移的起点。
【代码2】:

 1 //type code which was others
 2
 3 #include <vector>
 4 #include <cstring>
 5 #define clr(abc,z) memset(abc,z,sizeof(abc))
 6 using namespace std;
 7 const int inf = 1e9;
 8
 9 class BubbleSortWithReversals{
10 public:
11     int n;
12     int dp[110][110], A_[110];
13     int f1[110][110], f2[110][110];
14     int getMinSwaps(vector <int> A, int K){
15         int n = A.size();
16         for(int i = 1; i <= n; i++)
17             A_[i] = A[i - 1];
18         for(int i = 1; i <= n; i++)
19         for(int j = i; j <= n; j++){
20             for(int k = i; k <= j; k++)
21                 for(int p = k + 1; p <= j; p++)
22                     if(A_[k] > A_[p])
23                         f1[i][j]++;
24             for(int k = i; k <= j; k++)
25                 for(int p = i; p < k; p++)
26                     if(A_[k] > A_[p])
27                         f2[i][j]++;
28             for(int k = i; k <= j; k++)
29                 for(int p = j + 1; p <= n; p++)
30                     if(A_[k] > A_[p])
31                         f1[i][j]++, f2[i][j]++;
32         }
33         clr(dp, 10); dp[0][0] = 0;
34         for(int i = 1; i <= n; i++)
35         for(int j = 0; j <= K; j++)
36         for(int k = 0; k < i; k++){
37             dp[i][j] = min(dp[i][j], dp[k][j] + f1[k + 1][i]);
38             if(j) dp[i][j] = min(dp[i][j], dp[k][j - 1] + f2[k + 1][i]);
39         }
40         int ans = 1e9;
41         for(int i = 0; i <= K; i++)
42             ans = min(ans, dp[n][i]);
43         return ans;
44     }
45 };

【说明】:
这两个解法的代码均是比赛后参照他人的(基本属于对拍)。
现在在博客上写文章,我绝不会无意义地追求文章数量,我一定会尽力让自己的每一篇报告写得很细致。而且不足的地方以后回顾时会定

期维护,一定。

SRM627 Div2 1000 DP,布布扣,bubuko.com

时间: 2025-01-21 21:19:43

SRM627 Div2 1000 DP的相关文章

SRM632 Div2 1000 DP

[题意]: 给你一个值v和一组数字,求在这组数字中有多少种可用的组合,可用的组合意思是组合中数字乘积等于这个v(不同位置的数组成的相同情况视为不同的解).[知识点]:DP[题解]: 声明map<LL, LL> mp; 第一个元素代表能整除v的一个数,第二个元素代表数字组前i个元素中乘积为该数的集合数. 然后遍历这组数字,mp[v]的值即为答案. 当v为1时,要特判减1. [代码]: 1 #include <cstdio> 2 #include <string> 3 #i

TOPCODER SRM 686 div2 1000

// TOPCODER SRM 686 div2 1000 Problem Statement 给出一个至多长 100 的字符串,仅包含 ( 和 ),问其中有多少个不重复的,合法的括号子序列. 子序列可以不连续:合法即括号序列的合法:答案模 1,000,000,007. Examples "(())(" Returns: 2 Correct non-empty bracket subsequences are "()" and "(())". &

Topcoder SRM 648 Div2 1000

Problem 给一个长度为N的字符串S,S只含有'A'.'B'.'C'三种元素.给定一个K,要求返回字符串S,使得S中恰好有K对pair(i,j)满足 0=<i<j<N,且 S[i]<S[j].若不存在,则返回空串. Limits Time Limit(ms): 2000 Memory Limit(MB): 256 N: [3, 30] K: [0, N*(N-1)/2 ] Solution 设S中含有n1个'A',n2个'B',n3个'C',设num=n1*n2+n1*n3+n

Topcoder Srm 673 Div2 1000 BearPermutations2

\(>Topcoder \space Srm \space 673 \space Div2 \space 1000 \space BearPermutations2<\) 题目大意 : 对于一个长度为 \(n\) 的排列,定义其的贡献为对其建笛卡尔树,树上有两个儿子的节点其左右儿子在原排列中的距离之和,给出 \(n, Mod\),求所有长度为 \(n\) 的排列的贡献之和对 \(Mod\) 取模的值 \(1 \leq n \leq 100\) 解题思路 : 考虑一个最暴力的 \(dp\) ,设

Topcoder SRM 654 Div2 1000

Problem 给一个长度为N(N∈[1,2000])的数列An(An∈[?100,100]),设ans=A1?A2?...?An,下面进行M(M∈[1,2000])次操作,每次将A的p[i]的值修改为v[i],即A[p[i]]=v[i],每次只允许加入最多2个括号进入ans等式,问ans的最大值可以是多少? Solution dp.. 设dp[i][j]表示从 1 到 i-1 已经有 j 个括弧时候的最大值,j=0时表示没有括弧,j=1时表示(这样子,j=2时表示(( 这个样子,j=3时表示(

SRM 628 DIV2 1000 CandleTimerEasy 状态压缩+DFS

题意:给你一个树型蜡烛,你可以从1个或多个叶子开始点蜡烛,问你能使蜡烛烧完以后能得到时间的个数. 解题思路:状态压缩枚举DFS, 解题代码: 1 // BEGIN CUT HERE 2 /* 3 4 */ 5 // END CUT HERE 6 #line 7 "CandleTimerEasy.cpp" 7 #include <cstdlib> 8 #include <cctype> 9 #include <cstring> 10 #include

SRM631 Div2 1000 ???

保存代码,题解待更新... 1 #include <cstdio> 2 #include <string> 3 #include <vector> 4 #include <map> 5 using namespace std; 6 7 #define LL long long 8 9 class TaroCoins{ 10 public: 11 long long getNumber(long long N){ 12 LL pushOne = 0, notP

Topcoder SRM646 DIV2 1000

题目大意:给你两个vector,一个代表队伍的得分,一个代表得分为这个得分的队伍的个数,假设每支队伍还有两次比赛没有进行,比赛的规则是赢一场得3分,输一场得0分,没有平的情况.已知这个队伍的目前得分让你求出来所有的球队打完最后一场,这个球队理论上的最好排名,注意可以和除了自己以外的任何一支队伍进行比赛,次数任意. 解题思路:显然排名高的话,这个球队这两次比赛一定得赢,所以主要分为3种情况:1,即使得分+6也不会超过的,这些球队即使全输也会在他们前面,所以我们让他们全赢,2,低于m的,这些球队即使

tc srm659 div2 1000

题目: Garth likes apples and oranges. Recently he bought N fruits, where each fruit was either an apple or an orange. Then he ate all N fruits in some order. You are given an int K. Garth observed that at every point in time, if he made a list of the l