UVa 10618 Fixing the Great Wall
题目:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36139
思路:
数轴上有n个点需要修复,每个点有信息c,x,d 表示位于x且在t时修缮的费用是c+d*t,找一个修缮序列使n个点能全部修缮且有费用最小。
可以发现:在任意时刻,修缮完的点都是连续的,因为修缮不需要时间,将一些点“顺手”修缮了肯定不差。
d[i][j][k],表示已经将i-j个点修缮完后位于p(if (k==0) p=i;else p=j;)的花费,不知道时间怎么办呢?
:注意到每个点的花费是c+d*t ,累计花费并不一定要将每个点的花费都求出来然后相加。于是正式定义d表示该清情况下所有已知花费,每当因为移动时间耗去t所有没有访问的点u花费都会相应增加u.d*t,总花费增加sum_d*t。换句话说,这种方法根据花费的特点将单个点花费累加变成了累计未访问点在未访问时间内的花费。
当位于ijk时有两种抉择:左走i-1 j 0 或右走 i j+1 1 这就是子问题 。
答案不大于10^9 但过程中好像input中会出现超int的情况但long long空间占用太大,因此用了double,最后转成int。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 6 using namespace std; 7 8 const int maxn = 1000 + 10; 9 const int INF = 1e30; 10 11 struct Node{ 12 double x,c,d; 13 bool operator <(const Node& rhs) const { 14 return x<rhs.x; } 15 }; 16 Node nodes[maxn]; 17 18 int n,kase; 19 double v,x,d[maxn][maxn][2]; 20 //d记录目前已知的所有花费 21 double pre_d[maxn]; 22 int vis[maxn][maxn][2]; 23 24 inline double cost(double x,double y,int i,int j) { 25 double finished=0; 26 if(i>j) return 0.0; 27 if(i>=0 && j>=0) finished += pre_d[j]-pre_d[i-1]; //sum_d[i,j] 28 return (pre_d[n]-finished) * fabs(y-x)/v; //总花费+所有未访问的点.d*t 29 } 30 31 double dp(int i,int j,int k) { 32 if(i==1 && j==n) return 0; // 边界 33 double& ans=d[i][j][k]; 34 if(vis[i][j][k]==kase) return ans; 35 vis[i][j][k]=kase; //记忆化搜索 36 37 ans=INF; 38 double x=(k==0? nodes[i].x:nodes[j].x); 39 if(i>1) ans=min(ans,dp(i-1,j,0) + cost(x,nodes[i-1].x,i,j)); 40 if(j<n) ans=min(ans,dp(i,j+1,1) + cost(x,nodes[j+1].x,i,j)); 41 return ans; 42 } 43 44 int main() { 45 kase=0; 46 memset(vis,0,sizeof(vis)); 47 while(scanf("%d%lf%lf",&n,&v,&x)==3 && (n&&v&&x)) { 48 ++kase; 49 double sumc=0; 50 FOR(i,1,n+1){ 51 scanf("%lf%lf%lf",&nodes[i].x,&nodes[i].c,&nodes[i].d); 52 sumc += nodes[i].c; 53 } 54 sort(nodes+1,nodes+n+1); 55 56 pre_d[0]=0; 57 FOR(i,1,n+1) pre_d[i]=pre_d[i-1]+nodes[i].d; 58 59 //'哨兵' 如果初始点在第0个点的左或第n个点的右 //使满足判断的普遍性 60 nodes[0].x=-INF; 61 nodes[n+1].x=INF; 62 double ans= INF; 63 FOR(i,1,n+2) 64 if(x>nodes[i-1].x && x<nodes[i].x){ //找到起点位置处于i-1...i 然后移动向左|右点 65 if(i>1) ans=min(ans,dp(i-1,i-1,0)+cost(x,nodes[i-1].x,-1,-1)); 66 if(i<=n) ans=min(ans,dp(i,i,0)+cost(x,nodes[i].x,-1,-1)); 67 break; 68 } 69 printf("%0.lf ",floor(ans + sumc)); 70 } 71 return 0; 72 }