工作区的颜值选择(中等)
中等版本的此题https://nanti.jisuanke.com/t/A1016
书接上文,这道题的中等版本无改动,只是规定了座位n=2,一共两行,而m列数的限制加到了<=30。
此时最多有60个位置,暴力破解出所有排列组合的话最多要60^60这种程度,再怎么剪枝也没用了吧(废话)。
此时采用动态规划(DP)法,思路核心是找到状态方程,想明白都有哪些个状态,状态之间是怎么转移的。
最好是顺着当前结构最优解是总体最优解的一部分这个思路来用递推或者递归来写出具体代码。
对于此题,一共有60个座位,如果把他们标号后有了先后的顺序,那么就会有60(位置)*60(该位置颜值可能取值)个状态。又因为前一个状态和这个状态之间存在着
DP[p][i][j] = max(DP[p][i][j], DP[p][i - 1][xx] + C[p][i][jj] + abs(j + x));这个关系其中p为行数,i为列数,而j为这个坐标上的颜值k的取值,x为上个坐标的颜值k的取值。xx是x的总消耗结果存储位置的序号,C为当前参数带入颜值部分计算公式得出的该位置的花费。
可以看出,每次转换一个状态要对于当前状态的j把上一个位置的所有状态去个遍才会知道。所以复杂度为k*k*m*n。
所以数据量才60*60*60?恕我直言,就算再多乘两个60都能稳过(也就六七亿)。。。。。。。。这么想着然后才错了,所以以上都是错误思路。。。
上边那句当我没说,官方题解里说7亿会超时,目前也不知道会不会。
代码里贴了不知道对错的总结反思,有时间找老师验证一下。正确的明天再更把,今天肚子里没货了ORZ.
国际惯例,下面先列出我的错误代码。
/* 360硅谷范的工作区被划为了 n 行 m 列的连续工位,每个工位上可以安排一名员工工作。当第 i 行第 j 列(行和列都从1开始编号)的工位中坐着一个颜值为 C 的员工时,公司会需要付出的成本是 (|C + i + j| ⊕ U) * W(其中⊕是按位异或符号)。 U 是这个工位的风水指数,W 是这个工位上接收到360安全路由发出的wifi信号强度。 比较有意思的是,两个相邻的工位(同行相邻列或同列相邻行)上坐着颜值为 C1 和 C2 的员工时,公司会额外付出 |C1 + C2| 的成本。 找到一种方案,决定每个工位上的员工应有的颜值 C (|C| ≤ k),使得公司最终需要付出的总成本最小。并输出需要付出的最小总成本。 输入格式 第一行输入三个整数 n, m, k (1 ≤ k ≤ 30) 分别表示工区的长、宽、和颜值绝对值的上限。 接下来读入两个 n 行 m 列的整数矩阵。 第一个矩阵表示每个工位的风水指数 Ui,j (1 ≤ Ui,j ≤30)。 第二个矩阵表示每个工位收到360安全路由发出的wifi信号强度Wi,j (1 ≤ Wi,j ≤30 且 Wi,j 为整数)。 对于简单版本,1 ≤ n ≤ 2,1 ≤ m ≤ 3; 对于中等版本,n = 2, 1 ≤ m ≤ 30; 对于困难版本,1 ≤ n, m ≤ 30。 输出格式 输出一个数,表示公司需要付出的最小成本。 样例1 输入: 2 2 2 1 0 1 0 1 1 1 1 输出: 11 提示信息 样例的填充方案如下: -1 1 0 -1 */ #include<iostream> #include<string.h> #include<algorithm> #define rep(i,n,t) for(int i=(n);i<(t);i++) #define mms(a,b) memset(a,b,sizeof(a)) #define M 50 #define Maxm 35 #define N 100 int C[2][Maxm][N], DP[2][Maxm][N], wifi[2][Maxm], fengshui[2][Maxm]; using namespace std; int main() { int n, m, k; mms(C, 0); mms(DP, 0); mms(wifi, 0); mms(fengshui, 0); cin >> n >> m >> k; rep(i, 0, 2) rep(j, 0, m) cin >> fengshui[i][j]; rep(i, 0, 2) rep(j, 0, m) cin >> wifi[i][j]; rep(i, 0, 2) rep(j, 0, m) rep(z, -k, k + 1) { int kk = z + M; C[i][j][kk] = (abs(kk + i + j + 2) ^ fengshui[i][j]) * wifi[i][j]; } rep(i, 0, m) { rep(p, 0, 2) { rep(j, -k, k + 1)//代表本层i的取值。 { rep(x, -k, k + 1)//代表每个上一层最大值。 { int jj = j + M, xx = x + M; if (p == 0) { DP[p][i][j] = max(DP[p][i][j], DP[p][i - 1][xx] + C[p][i][jj] + abs(j + x)); } else rep(y, -k, k + 1) { int yy = y + M; DP[p][i][j] = max(DP[p][i][j], DP[p - 1][i-1][xx]+C[p][i][]+C[p][i][jj]+abs(y+j)+abs(j + x)); } } } } } return 0; } //错了,本来觉得可以用【单点坐标x】【y】【2k】来作为动态规划状态DP数组。时间复杂度应该和将一列结点捆绑起来的算法实现一样都是O(m*k^3)。 //因为可以想到状态为 2m,每次的决策数量为k,即该xy上该取什么值,决策时间量0行的元素为k个(只与其左边元素相关),而1行将为k^2个(与左和上同时相关) //结果发现这样列的话状态方程就有问题 预想 以这个顺序1 3 5……走到底 // 2 4 6…… // //当走到第一行元素时的方程为DP[p][i][j] = max(DP[p][i][j], DP[p][i - 1][xx] + C[p][i][jj] + abs(j + x));。 //第二行元素的情况就开始错了,应该是DP[p][i][j] = max(DP[p][i][j], DP[p-1][i - 1][yy] +C[p][i][xx]+ C[p][i][jj] +C[p][i][pp]+ abs(j+p)+abs(j + x)+abs(x+p)+(y+p)); //就是要假如在4则要退回1再同时选择23和4搭配,按顺序4要枚举3和2中情况,再与自身的2k种状态结合取最小值来决策,但因为4不可以同时配2和3, //因为3的2k种状态取最小值过程就是由2推出的。所以有可能存在与4搭配后发现没有使用3中最优解的可能,然而这与状态转移方程相违背了。 //可能会造成5在取值过后,3最优状态中的参数与5最优状态中的参数不一样,所以不对,原因就是刚开始的时候状态转移方程就没有彻底分析对。