今天是参加讲授的第一天。上午主要学习了一部分的DP选讲和线段树的运用,下午是练习测试。这里将今天所学的知识点做一个清理和总结消化。
上午 - DP选讲
Part 1
酱神寻宝(CDOJ1141)
题意:
酱神来到了一座小岛,岛上有n个箱子。
一共有3中不同的钥匙,金钥匙、银钥匙和万能钥匙。酱神一开始有a把金钥匙、b把银钥匙和c把万能钥匙。
第i个箱子上有xi把金锁,yi把银锁。金钥匙只能打开金锁,银钥匙只能打开银锁,万能钥匙两种锁都能打开。用于打开锁的钥匙会立刻损坏,酱神会丢掉损坏的钥匙。箱子里有ai把金钥匙、bi把银钥匙和ci把万能钥匙,想要取出箱内的钥匙必须要打开这xi+yi把锁。
酱神的目的是使他拥有的钥匙总数最多。一旦酱神认为自己已经拥有了最多的钥匙,他就不会去开剩下的箱子了。
思路:
金钥匙和银钥匙都具有指定性,只能打开特定的锁。所以在相同的情况下,我们尽可能地保留万能钥匙(贪心策略)。
则将这个策略运用于DP,我们定义状态为 dp[st][j] 。st是一个二进制数,利用状压保存了箱子的状态,1表示该位对应的箱子已经打开,0表示还没有打开。j保存的是当前酱神持有的金钥匙的数目。dp储存了当前酱神持有的最多的万能钥匙的数目。而银钥匙可以通过金钥匙数目、万能钥匙数目和st的状态计算得到。
每次状态转移时,从st中枚举当前为零的位数(宝箱编号),依次置一,从而得到新状态。
Part 2
Where is Bob(HDU5208)
题意:
Alice从[l1,r1]中选出一个数字x,JSL看到这个数字之后从[l2,r2]中取出一个数字y(y可能和x相同)。最后他们计算出数字z=x⊕y(⊕为异或运算)。Alice希望z尽可能大,而JSL希望z尽可能小。Alice和JSL都很聪明,那么z最后会是多少?
PS:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
思路:
游戏存在明显的先后顺序,Alice已知JSL是会尽可能地选一个数使z变小。所以我们可以想出暴力程序:首先枚举 i ,表示Alice能选择的数 ( l1 ≤ i ≤ r1 );而后枚举 j ,表示JSL能选择的数( l2 ≤ j ≤ r2 )。则每次在(r2 - l2)个 j 中选出能使z最小的 j (此处模拟了JSL的选择),然后在(r1 - l1)个 i 中确定出能使z最大的 i (Alice的选择)。从而得到要求的z。
不过这么做,复杂度很高,是O(n²)。
正解是DP,利用位运算。每次从高位决策(因为高位比低位权重更大,即在高位置一、低位全置零的效果大于高位置零、低位全置一),分别缩小两人所选的数的上下界。定义dp[i][l1][r1][l2][r2] ,i 表示当前操作到了第 i 位,l1 和 r1 分别是Alice能选择的数的下届和上届,l2 和 r2 分别是JSL能选择的数的下届和上届。dp中储存了最后的价值z的具体值。
状态转移时,每次逐位决策。首先是Alice拥有主动权,如果上下界允许,我们的第 i 位可以在0和1中决策(即Alice通过对JSL的选择的预测决定自己选择的最优(即z最大))。
如果选1,则上界的所有比 i 低的位数全部置零;反之,如果选0,下届所有比 i 低的位数全置一。这样可以不断缩小上下界的范围。而后轮到JSL,JSL会尽可能地选择与Alice不同的数,则我们对他的上下界也进行相同的操作。直到最后经过末位,得到确定的上下界和z的值(即dp[0][l1][r1][l2][r2]的值)。
Part 3
酱神的旅行(CDOJ1140)
题意:
酱神要去一棵树上旅行。
酱神制定了一个旅行计划,他要按顺序去m个树上的结点,a1,a2,a3,...,am。
酱神有一辆车,树上的每一条边既可以开车通过,也可以走过去,两种方法需要不同的时间。如果选择走路,酱神需要先把车停在结点上,在他下一次要开车的时候,必须先回到停车的结点取车。
酱神和他的爱车一开始都在a1结点上,酱神要依次访问完这m个结点最少需要多少时间。
思路:
首先这题看上去像是一道树形DP,似乎需要按照树的路径进行遍历。但实际上需要去的节点的顺序是规定了的,也就说酱神的旅行是有规定的路径的。所以先暴力dfs将路径求出来,保存在数组中,再到数组里进行DP。
定义状态dp[i][j], i 表示当前酱神在第 i 个节点,j表示是否有车,dp表示在 i j 描述的情况下的路径最小值。则针对第 i 号节点的更新有两种选择,第一是当前没有车的情况,则dp[i][0] = min(dp[i - 1][0] , dp[i - 1][1]) + walk_cost[i];而当前有车的话,又要分两种情况讨论,一种是当前有车是有 i-1 开车到达 i 节点(dp[i][1] = dp[i - 1][1] + car_cost[i]),另一种是之前没有车到达 i 节点时捡到了车。针对捡到了车的情况,能在 i 捡到车,一定是在之前将车停在了与i相同的点上,而且该点一定在i-1的根节点上,但是经过的与i相同的点可能有多个,我们将其记为j1,j2,j3……jk。则我们可以维护一个前缀和sum[i],表示走路到前第i个点的代价。则有dp[i][1] = min (dp[jk][1] + sum[i] - sum[jk])