题意: 在连续的 n 秒中,每秒会出现 m 个龙珠,出现之后会立即消失,知道了第一秒所在的位置,每从一个位置移动到另一个位置的时候,消耗的价值为
abs(i-j), 知道了次出现的龙珠的价值,问 n 秒之后得到的最大价值是多少。
分析: 当 pos[i][j] >= pos[i-1][k] 时:dp[i][j]= min( dp[i-1][k] - pos[i-1][k] ) + pos[i][j] + w[i][j];
当 pos[i][j] <= pos[i-1][k] 时:dp[i][j]= min( dp[i-1][k] + pos[i-1][k] ) - pos[i][j] + w[i][j];
遇到 dp[i]= max/min( b[i-1] )+ a 形式时,可考虑单调队列,这里可以事先对每秒中出现的位置排好序,这样就可以降维。
#include <stdio.h> #include <string.h> #include <queue> #include <algorithm> #include <math.h> using namespace std; #define INF 0x1f1f1f1f #define clr(x)memset(x,0,sizeof(x)) int min(int a,int b) { return a<b?a:b; } int ab(int x) { return x>0?x:-x; } struct node { int pos, w; }b[60][1005]; int dp[60][1005]; int q[1005]; bool cmp(node a,node b) { return a.pos<b.pos; } int main() { int i, j, k; int t, x, n, m; scanf("%d",&t); while(t--) { scanf("%d %d %d",&n, &m, &x); for(i = 0; i < n; i++) for(j = 0; j < m; j++) scanf("%d",&b[i][j].pos); for(i = 0; i < n; i++){ for(j = 0; j < m; j++) scanf("%d",&b[i][j].w); sort(b[i],b[i]+m,cmp); } for(i = 0; i < m; i++) dp[0][i] = ab(x-b[0][i].pos)+b[0][i].w; int dmin; for(i = 1; i < n; i++){ k = 0; dmin = INF; for(j = 0; j < m; j++){ while(k < m && b[i][j].pos >= b[i-1][k].pos){ dmin = min(dp[i-1][k]-b[i-1][k].pos,dmin); k++; } dp[i][j] = dmin + b[i][j].pos + b[i][j].w; } k = m-1; dmin = INF; for(j = m-1; j >= 0; j--){ while(k >= 0 && b[i][j].pos <= b[i-1][k].pos){ dmin = min(dp[i-1][k]+b[i-1][k].pos,dmin); k--; } dp[i][j] = min(dp[i][j],dmin-b[i][j].pos+b[i][j].w); } } dmin = dp[n-1][0]; for(i = 1; i < m; i++) dmin = min(dmin,dp[n-1][i]); printf("%d\n",dmin); } return 0; }