1 条题解
-
0
自动搬运
来自洛谷,原作者为

zpy12345
普普通通搬运于
2025-08-24 22:31:18,当前版本为作者最后更新于2025-08-11 16:29:03,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
[COCI 2012/2013 #1] MARS
本篇题解的思路应该是效率最高的思路。
思路
这么复杂的题目,当然要用 DP 了。
首先,我们可以依照常规设出状态 表示对前 个细菌进行编号,最小的长度。显然无法转移。我们注意到,最小的长度在转移时,需要知道上一个位置是什么值,才好求出排斥值进行转移。故设 表示对前 个细菌进行编号,其中最后一个细菌(即位置 的细菌)编号为 ,最小的长度。
可以发现,如果对编号后的序列按下标每两个划分为一组,那么编号为 的细菌一定在同一组。如果不是,例如在 中, 且 ,那么 ,。这里 为偶数, 为奇数,显然无法刚好填满,与题意矛盾。
故如果对编号后的序列按下标每两个划分为一组,那么编号为 的细菌一定在同一组。同理可证:
如果对编号后序列的序列按下标每 个进行划分,那么编号为 $\{1,2,\cdots,2^x-1,2^x\},\{2^x+1,2^x+2,\cdots,2^{x+1}-1,2^{x+1}\},\cdots,\{2^K-2^x+1,2^K-2^x+2,\cdots,2^K-1,2^K\}$ 的细菌一定在同一组。
有了以上结论,可以发现,如果第 位编号为 ,那么第 位的取值就有一定范围。例如在 $\{a_{1},a_{2},a_{3},a_{4},a_{5},a_{6},a_{7},a_{8}\}$ 中,若 ,则的取值范围为 , 与 的取值范围为 , 的取值范围为 。
至于怎么求出取值范围,结合代码以及注释理解更佳。
转移就很好写了:
$$dp_{i,j}\longrightarrow dp_{i+1,p}(p\in [l_{i,j},r_{i,j}]) $$其中 表示在第 位编号为 的情况下下一位的取值范围。
时间复杂度 。
::::info[时间复杂度证明]
自己证。结合代码,发现复杂度主要分为两部分:
- 求取值范围:求取值范围时每次扩大一倍,故为 ,即 。在乘上外层循环即为 。
- 转移:$$\sum_{i=0}^{2^K-1} 2^K(r_{i,j}-l_{i,j}+1)=2^K\sum_{i=0}^{2^K-1} (r_{i,j}-l_{i,j}+1)$$
手推一下,发现有 的位置取值范围大小为 ,有 的位置取值范围大小为 ……有 的位置取值范围大小为 ,还有第一个点的取值范围大小为 。
所以 $$2^K\sum_{i=0}^{2^K-1} (r_{i,j}-l_{i,j}+1)=2^K\sum_{i=1}^{K} 2^K\frac{1}{2^i}2^{i-1} =2^KK2^{K-1}=2^{2K-1}K$$
转移时间复杂度为 。
相加得总复杂度为 。 ::::
代码
跑的还是蛮快的。
#include<bits/stdc++.h> using namespace std; int k,a[515][515]; int dp[515][515]; int main() { scanf("%d",&k); k=(1<<k); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) scanf("%d",&a[i][j]); memset(dp,127,sizeof(dp)); for(int i=1;i<=k;i++) dp[1][i]=0; for(int i=1;i<k;i++) for(int j=1;j<=k;j++) { int l=j,r=j; int ll=i,rr=i; //表示[ll,rr]区间里每个数的取值范围均为[l,r](可能有部分数取值范围更小,但是不影响) //如果当前区间为第偶数个区间,那么可以知道其前面那个区间的范围 //否则,可以知道后面那个区间的范围 while((rr/((rr-ll+1)))%2==0) { ll=rr-2*(rr-ll+1)+1; //区间扩大一倍 if((r/((r-l+1)))%2==0) //同理 l=r-2*(r-l+1)+1; else r=l+2*(r-l+1)-1; } int yl=l,yr=r;//记录原先的l,r if((r/((r-l+1)))%2==0) l=r-2*(r-l+1)+1,r=yl-1; else r=l+2*(r-l+1)-1,l=yr+1; for(int p=l;p<=r;p++) dp[i+1][p]=min(dp[i+1][p],dp[i][j]+a[j][p]); } int ans=1e9; for(int i=1;i<=k;i++) ans=min(ans,dp[k][i]); cout<<ans; return 0; }
- 1
信息
- ID
- 6723
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者