ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解:星空

2021-06-29 16:31:26  阅读:134  来源: 互联网

标签:星空 题解 len 差分 序列 操作 define dis


  首先序列A中存在若干个0与若干个1(假定0代表灯亮,1代表灯灭),目标是通过若干次操作使得序列所有元素均为0

每次操作可以从给定的m种长度中选择一种,将序列中任意一段长度为选定长度的子序列取反,求最有情况下至少需要多少次操作

  考虑分治问题,首先根据数据范围,若在每次操作是均以O(len)复杂度对序列取反必然超时,也就是说首先需要优化取反操作

类似于T1入阵曲,区间取反也是对范围信息的查询(操作),考虑对于范围信息进行维护的两种预处理手段:前缀和与差分,

发现利用差分我们可以在O(1)复杂度对区间进行取反,那么问题主体就由原序列转移到差分序列。

  长度为n的序列中,存在不超过2k个1,求使得序列所有元素均为0的最小操作次数。问题元素很少,在解决完区间取反问题后

就要考虑每次操作的效果,很显然每次操作我们可以选取区间上一定距离的两点进行取反。考虑为了是的操作次数最少,每次只需

对两个位置为1,0或1,1进行操作(因为对0,0进行操作无意义),若1,1进行操作则二者均消失,若1,0进行操作则二者互换

位置,发现问题进一步转化,在可供选择的距离下,如何经过最小操作次数使得所有1相互移动最终消失,因为只有有限种距离可

供选择,故我们发现任意两个1消失所需最小距离是一定的,通过bfs可以预处理得到。

  最终问题呈现形式为,存在不超过2k个物品,已知任意2个物品消失代价,如何选择使得所有物品消失总代价最小,很显然可以

通过状压DP解决。

  最终考虑一下几个问题:

  首先差分序列所有为0如何保证原序列全为0而非全为1(其实可以模拟,但思考原理更重要)。考虑差分序列对于本题的意义,其

代表原序列的不同性,一个被我们忽视的重要潜条件为:在进行差分时我们默认0号灯亮!这意味着从0号灯后若差分序列为1则代表

灯灭。此时在默认0号灯亮与差分意义的双重制约下保证操作完成时原序列为0;其实也可以考虑为在保证最优情况下,由于主观参与会

保证只对1进行操作,但是貌似并不严谨。

  第二考虑时间复杂度优化问题,也是常见的最优化问题的等效手段。在最后进行DP是我们显然可以k^2枚举所有情况取min,然而

时间复杂度并不保险,考虑问题仅需求出最终最优结果,我们并不需要保证每个状态的最优性,因此我们只需要使得问题不断向最终结果

拓展即可。另一种理解为,在状态转移时发现有很多冗余情况,为了优化复杂度,我们在枚举时进枚举当前状态对以后第一个造成影响的

状态转移即可,可以发现能枚举到所有情况,详见代码(注意理解应用最优化问题优化部分的代码):

 1 #include<bits/stdc++.h>                                 //注意差分序列范围为1-n+1或0-n,分别对应不同的修改方式
 2 using namespace std;                                    //为了便于DP采取0-n,修改方式为l-1,r
 3 #define P pair                                          //sta作用为虚拟建图以及还原节点
 4 #define Q queue
 5 #define I int
 6 #define LL long long
 7 #define B bool
 8 #define C char
 9 #define RE register
10 #define L inline
11 I n,k,m,cnt,len[64];
12 LL dis[16][40005],DP[1<<16];
13 B state[40005];
14 P<I,I> sta[16];
15 Q<I>q;
16 L I read(){RE I x(0);RE C z(getchar());while(!isdigit(z))z=getchar();while(isdigit(z))x=x*10+(z^48),z=getchar();return x;}
17 signed main(){
18     n = read(); k = read(); m = read();
19     for(RE I i(1);i <= k; ++ i)  state[read()] = 1; 
20     for(RE I i(0);i <= n; ++ i)  if(state[i] ^ state[i + 1]) sta[cnt] = make_pair(cnt,i),cnt++;
21     for(RE I i(0);i < m; ++ i)  len[i] = read();
22     for(RE I i(0);i < cnt; ++ i){
23       I fir(sta[i].first),sec(sta[i].second);
24       for(RE I j(0);j <= n; ++ j) dis[fir][j] = INT_MAX;
25       q.push(sec); dis[fir][sec] = 0;
26       while(!q.empty()){
27         I x(q.front()); q.pop();
28         for(RE I i(0);i < m; ++ i){
29           if(x - len[i] >= 0 && dis[fir][x - len[i]] > dis[fir][x] + 1)
30             dis[fir][x - len[i]] = dis[fir][x] + 1,q.push(x - len[i]);
31           if(x + len[i] <= n && dis[fir][x + len[i]] > dis[fir][x] + 1)
32             dis[fir][x + len[i]] = dis[fir][x] + 1,q.push(x + len[i]);
33         }  
34       }  
35     }
36     memset(DP,0x3f,sizeof(DP)); DP[0] = 0;
37     for(RE I i(0),x(0);i < 1 << cnt; ++ i,x = 0){
38       while(i & 1 << x) x++; 
39       for(RE I j(x + 1);j < cnt; ++ j)
40         if(!(i & 1 << j))  DP[i|1<<x|1<<j] = min(DP[i|1<<x|1<<j],DP[i] + dis[x][sta[j].second]);
41     }
42     printf("%lld",DP[(1<<cnt)-1]);
43 }
View Code

 

标签:星空,题解,len,差分,序列,操作,define,dis
来源: https://www.cnblogs.com/HZOI-LYM/p/14950687.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有