1. 程式人生 > >【URAL 1900】Brainwashing Device(預處理區間和+DP)

【URAL 1900】Brainwashing Device(預處理區間和+DP)

題目大意:
n個城市1~n,第i座城市與i+1有一條路。
第i個城市往i+1~n每座城市都有客流量,每個人如果從i->j,要經過i與j間每一條邊一次。
政府決定縮短k條道路的距離(1 <= k <= n-1)

對於每個人,從i到j,如果中間任何一條道路被縮短,他會be happy。
問最多讓多少人be happy,然後輸出縮短的道路編號,編號i表示i->i+1這條路

考慮dp[i][j]表示縮短邊i,同時總縮短了j條邊的最大happy值。

再一個存它的前驅。

這樣最後輸出遞迴輸出即可。

典型的路徑記憶dp,ural後面這類題還真是多……

不過有點噁心的是轉移。dp[j][l-1]->dp[i][l],增加的happy值是j+1~i這幾座城市,通往i+1及後面的城市的客流量。。。

我做法是dp列舉j的過程中累積,這樣只需要記錄每個點i,通往點j及以後城市的客流量,輸入完倒著走一邊就可以了。

程式碼如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list> #include <algorithm> #include <map> #include <set> #define LL long long #define Pr pair<int,int> #define fread(ch) freopen(ch,"r",stdin) #define fwrite(ch) freopen(ch,"w",stdout) using namespace std; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; const
double eps = 1e-8; const int maxn = 555; int dp[2][maxn][maxn]; int tr[maxn][maxn]; void prt(int u,int k) { if(k == 0) return; prt(dp[0][u][k],k-1); printf("%d ",u); } int main() { fread("in.in"); //fwrite(""); int n,k,x; scanf("%d%d",&n,&k); for(int i = 1; i <= n; ++i) { for(int j = i+1; j <= n; ++j) scanf("%d",&tr[i][j]); for(int j = n-1; j > i; --j) tr[i][j] += tr[i][j+1]; } memset(dp,-1,sizeof(dp)); dp[0][0][0] = 0; dp[1][0][0] = 0; for(int i = 1; i < n; ++i) { int tmp = tr[i][i+1]; dp[0][i][0] = dp[1][i][0] = 0; for(int l = i-1; l >= 0; --l) { for(int j = 1; j <= k; ++j) { if(dp[0][l][j-1] == -1) break; if(dp[1][l][j-1]+tmp > dp[1][i][j]) { dp[0][i][j] = l; dp[1][i][j] = dp[1][l][j-1]+tmp; } } tmp += tr[l][i+1]; } } int id = 1; for(int i = 1; i < n; ++i) { if(dp[1][i][k] >= dp[1][id][k]) id = i; } printf("%d\n",dp[1][id][k]); prt(id,k); return 0; }