当当当,来更新,把本次kick start的三道题都补完了。回过头来发现,虽然没有我没有听过的算法,可是当时做的时候,确实是一个算法都没有想起来。看来,我的算法实力还是很弱,继续努力吧~
本道题考的是分组背包,但是还是不是单纯的板题,有一个坑需要通过在dp中加一维标志位来解决。废话不多说,上题。
【题意】一个人,一条街,N条狗。街用一维数组表示,人住在位置0,狗的位置可以是1,2,3,...。同一个位置可以有多条狗,每个狗有颜色。人穿带颜色的衣服,穿什么颜色的衣服就只能观察什么颜色的狗,如果想要观察和自己衣服颜色不同的狗,需要回家换衣服。人一开始在家中,每走一格耗费时间为1,观察狗和在家中换衣服不需要耗费时间。问这个人想要观察K条狗,至少需要耗费多少时间(注意,人在观察完最后一条狗时,不需要回家)。
【输入】第一行,T,代表用例个数。每个用例的第一行为N,K。N代表有多少条狗,K代表要观察多少条狗。第二行为N条狗的位置(乱序),第三行为N条狗对应的颜色(不一定从1开始)。
【输出】输出所需的最少时间
【小用例】
【input】
3 4 3 1 2 4 9 3 3 2 3 4 3 1 2 3 4 1 8 1 8 6 6 4 3 3 1 3 10000 1 2 8 9 5 7
【output】
Case #1: 8 Case #2: 6 Case #3: 10028
【解题思路】
#------------------------------------------- 我之前的暴力思路,可略过 ------------------------------------------------------------------------------------
我首先想到的暴力的思路就是使用dp[i][k]代表到以位置i结尾,观察k条狗,所需要的最短路程,我们用color[i]表示第i个的狗的颜色,pos[i]代表第i个狗的位置,状态转移为:
if color[i]==color[i-1]: dp[i][k] = min(dp[i][k], dp[j][k-1] + pos[i] - pos[j])
else: dp[i][k] = min(dp[i][k], dp[j][k-1] + pos[i] + pos[j])
但是,这样时间复杂度不够,是一个O(N^3)的算法,我们要找的是O(N^2)的算法。
我们看一下,多了哪一维,dp[i][j]是代表以位置i结尾,这就多记录了一个信息,如果代表的是前i个,这样就可以去掉一维,但是这样又不能记录颜色信息。因此,这里的i不能代表位置,要代表颜色。
以上就是我的想法,但是如果按照老的思路的话,还是一个O(N^3)的算法,我就想不下去了。在这里,我忽略了一个关键的因素。就是同一种颜色不同方案之间的不可重复性。
# -------------------------------------------------------正解思路--------------------------------------------------------------------------------------------------
对于某一种颜色(拿红色举例)来说,选择几条狗是我们的方案,观察完w条红色狗(w=0,1,2,3,...)所需要的时间可以看做是价值。对于红色的狗,我们只能选一种方案。也就是说,我们不能先观察3条红色的狗,再回家换衣服,观察绿色的狗,然后再换衣服,观察4条红色狗,因为这样的方案,一定不如直接观察7条红色的狗时间短。
因此,就转化成了一个分组背包问题,每种颜色的狗是一组,每组中,我们选择一种方案(选择观察前几条狗,这里的前面,指的是离家的远近)。dp[i][k]代表,组合到第i种颜色,观察k条狗,最短的时间;bag[i][w]代表对于颜色i,观察w条狗的最短距离(包含回家),num[i]代表颜色为i的狗的数量。最终的输出,为dp[C][K]。看一下状态转移:
dp[i][k] = min(dp[i][k], dp[i-1][k-w] +bag[i][w]) (w:1...min(K, num[i]))
需要注意的是,人最后不用回家。我的第一想法是,按照之前做过的homework的题,通过反推(dp[i][k] == dp[i-1][k-w]+bag[i][w])来实现,然后选最大的bag[i][w]就可以了。
这样的一个问题是,我们选择的dp[C][K]是包含回家的最优方法,并不是最后不用回家的最优方案。举个例子:
1
5 3
4 1 1 1 3
1 1 3 4 1
dp[C][K] = 6,选择观察第2,3,4条狗,最后一条狗不用回家,因此,答案为5.
但是,有更优的方案。如果选择官场第1,2,5条狗的话,虽然dp[C][K]为8>6,但是最终的答案为4。
可以看出,如果仅通过上述的状态设计,我们没法求出最优的方案。
其实,我们的状态缺了是否已经选择结束的标志位。dp[i][k][0]代表到第i种颜色,观察k条狗,观察完毕的最少时间。dp[i][k][1]代表尚未观察结束。状态转移为(伪代码):
dp[i][k][1] = min(dp[i][k][1], dp[i - 1][k - w][1] + bag[i][w]);
dp[i][k][0] = min(dp[i][k][0], dp[i - 1][k - w][1] + bag[i][w] / 2,dp[i - 1][k - w][0] + bag[i][w]);
我们最终的答案为dp[C][K][0]
最后要注意的是, 颜色没有从1开始按顺序排列,所以要使用map映射一下,就ok了
【AC代码】
// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//
#include
#include
#include
#include
#include