2015 ACM/ICPC Asia Regional Hefei Online I - Find a path(推公式+dp)
阿新 • • 發佈:2018-11-17
題目連結:傳送門
題意:在n*m的平面上,每個點都具有貢獻,問你從(1,1)到(n,m)找到一條路徑(只能往上或者往右),使得路徑上的點的方差最小。詢問最小的方差值為多少。
解決方法:首先根據簡化所給公式可以推出一個式子。推出式子的步驟如下
公式遞推很簡單。可以忽略。
下一步我們就可以用一個dp[i][j][k]來表示走到(i,j)時序列和為k(序列和不會超過(30+30-1)*30)時的平方和最小值,注意一下dp陣列的初始化,然後暴力更新就行了。
向上的方程:dp[i + 1][z][k + ax[i + 1][z]] = min(dp[i + 1][z][k + ax[i + 1][z]], dp[i][z][k] + ax[i + 1][z] * ax[i + 1][z]);
向右的方程:dp[i][z + 1][k + ax[i][z + 1]] = min(dp[i][z + 1][k + ax[i][z + 1]], dp[i][z][k] + ax[i][z + 1] * ax[i][z + 1]);
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<string> #include<iostream> #include<map> #include<vector> #include<set> #include<queue> using namespace std; const int inf = 0x3f3f3f3f; typedef long long ll; int ax[50][50]; ll dp[50][50][2000]; int main(void) { int t; scanf("%d", &t); int zzz = 1; while (t--) { int n, m; int tot = 0; scanf("%d%d", &n, &m); int maxx = 0; for (int i = 1; i <= n; i++){ for (int z = 1; z <= m; z++) { scanf("%d", &ax[i][z]); maxx = max(ax[i][z], maxx); } } printf("Case #%d: ", zzz++); for (int i = 0; i <= n; i++) { for (int z = 0; z <= m; z++) { for (int j = 0; j <= (n + m - 1)*maxx+10; j++) { dp[i][z][j] = inf; } } } dp[1][1][ax[1][1]] = ax[1][1] * ax[1][1]; for(int i=1;i<=n;i++){ for (int z = 1; z <= m; z++) { for (int k = 0; k <= (n + m - 1)*maxx + 10; k++) { if (dp[i][z][k] != inf) { if (i + 1 <= n) { dp[i + 1][z][k + ax[i + 1][z]] = min(dp[i + 1][z][k + ax[i + 1][z]], dp[i][z][k] + ax[i + 1][z] * ax[i + 1][z]); } if (z + 1 <= m) { dp[i][z + 1][k + ax[i][z + 1]] = min(dp[i][z + 1][k + ax[i][z + 1]], dp[i][z][k] + ax[i][z + 1] * ax[i][z + 1]); } } } } } ll ans = inf; for (ll i = 0; i <= (n+m-1)*maxx + 10; i++) { if (dp[n][m][i] != inf) { ans = min(ans, (n + m - 1)*dp[n][m][i] - i*i); } } printf("%lld\n", ans); } return 0; }