UVa 1336 Fixing the Great Wall

The Great Wall of China is truly one of the greatest wonders of the world. In 3-rd century BC, Emperor Qin Shi Huang connected the defensive structures built earlier by the states of Qin, Yan, and Zhao kingdoms. The purpose of the wall was to defend against raids by the barbarians from Mongolia and Manchuria. The wall was extended and renovated in later centuries, creating an impressive 6,700 km long fortification.

The centuries have left their mark on the wall, there are several sections that need immediate renovation. These sections have to be repaired as soon as possible since they deteriorate every day: if we do not fix them now, it will be more expensive to repair them later. Thus the Ministry of Monuments have designed and built the world's first Great Wall Automatic Repair Robot (GWARR), to repair the damaged sections (we are in the 21-st century, aren't we?) Your task is to write the software that will guide the robot and decide the order in which the sections are to be repaired.

For the purpose of this problem, we assume that the Great Wall is a long straight line, and every location on the wall is identified by a single number (say, the distance from one end). The GWARR is placed at some location on the wall and it can move with constant speed in both directions. For each damaged section you are given its location, how much it would cost to repair now, and how the cost would increase if repaired later. The GWARR works so efficiently that once it is at the exact location of the damaged section it can repair the wall immediately.

Input 

The input contains several blocks of test cases. Each case begins with a line containing three integers: an integer 1n1000, the number of damaged sections, an integer 1v100, the speed of the GWARR in distance units/time units, and an integer 1x500000, the initial position of the GWARR. The next n lines describe the n damaged sections that have to be repaired. Each line contains three integers: the location 1x500000 of the section, the cost 0c50000 of repairing it immediately, and 150000, the increase in cost per time unit. Therefore, if the section is repaired after t time units have passed, then we have to pay c + t units of money. It can be assumed that the locations of the sections are all different, and the initial location of the robot is not on the list of damaged seetions.

The input is terminated by a test case with n = v = x = 0.

Output 

For each test case, you have to output a line containing a single number, the minimum cost of repairing the wall. This number should be an integer, round down the result, if necessary. It can be assumed that the minimum cost is not more than 1000000000.

In the optimum solution for the first test case below, we first fix loeation 998 at the cost of 600, then the location 1010 at the cost of l400, and finally we fix the location 996 at the cost of 84, giving the total cost 2084.

Sample Input 

3 1 1000
1010 0 100
998 0 300
996 0 3
3 1 1000
1010 0 100
998 0 3
996 0 3
0 0 0

Sample Output 

2084
1138
 
  
 
  
#include 
#include 
#include 
#include 
using namespace std;

// record[i][j][0]代表现在在点i, 修复第1,...,i-1,j,,,,n点需要的最少花费
// record[i][j][1]代表现在在点j, 修复第1,...,i,j+1,,,,n点需要的最少花费
double record[1010][1010][2];
int flag[1010][1010][2];
// 修复点结构体
typedef struct node
{
	double x;	// 修复点位置
	double cost;	// 修复点花费价格
	double ratio;	// 修复点的每单位时间增长价格
	bool operator < (const struct node& s) const
	{
		return x < s.x;
	}
}node;

node array[1100];


int n;
double x;
double v;

double get_cost(int i, int j, double time);
double get_min(int i, int j, int k);


// cost_sum[i]代表第1,...,i的速率之和
double cost_sum[1100];

int c_count;

int main()
{
	memset(flag, 0, sizeof(flag));
	c_count = 1;	
	while(scanf("%d %lf %lf", &n, &v, &x) == 3 && !(n == 0 && v == 0 && x == 0))
	{
//		memset(record, 0, sizeof(record));
//		memset(flag, 0, sizeof(int)*(n*n*2));
		// 读入情况
		for(int i = 1; i <= n; i++)
		{
			scanf("%lf %lf %lf", &array[i].x, &array[i].cost, &array[i].ratio);
		}		

		// 修复点按位置从小到大排序
		sort(array+1, array+1+n);

		// 计算速率之和
		cost_sum[0] = 0;
		for(int i = 1; i <= n; i++)
			cost_sum[i] = cost_sum[i-1] + array[i].ratio;
		
		// 找到起始的位置
		int p1 = 0, p2 = 0;
		if(x < array[1].x)
		{
			p1 = 0;
			p2 = 1;
		}
		else if(x > array[n].x)
		{
			p1 = n;
			p2 = n+1;
		}
		else
		{
			for(int i = 1; i <= n-1; i++)
			{
				if(array[i].x < x && array[i+1].x > x)
				{
					p1 = i;
					p2 = i+1;
					break;
				}	
			}
		}	


//		printf("p1: %d p2: %d\n", p1, p2);

		double r1, r2;
		if(p1 >= 1 && p1 <= n)
		{
			r1 = get_cost(1,2, (x-array[p1].x)/v);
		}	
		if(p2 >= 1 && p2 <= n)
		{
			r2 = get_cost(1,2, (array[p2].x-x)/v);
		}

//		printf("r1: %lf  r2: %lf\n", r1, r2);
	
		// 计算结果
		double ans, a1, a2;
		if(x < array[1].x)
		{
			ans = r2 + array[1].cost + get_min(p1, p2, 1);
		}			
		else if(x > array[n].x)
		{
			ans = r1 + array[n].cost + get_min(p1, p2, 0);
		}
		else
		{
			a1 = r1 + array[p1].cost + get_min(p1, p2, 0);
			a2 = r2 + array[p2].cost + get_min(p1, p2, 1);
//			printf("a1: %lf, a2: %lf\n", a1, a2);
			ans = min(a1, a2);
		}
		printf("%.0f\n", floor(ans));
		c_count++;	
	}
	return 0;		
}


// 计算第1,...,i, j,...,n的所有点在time时间里产生的花费
double get_cost(int i, int j, double time)
{
/*
	double sum = 0;
	
	for(int k = 1; k <= i; k++)
		sum += array[k].ratio * time;
	
	for(int k = j; k <= n; k++)
		sum += array[k].ratio * time;

	return sum;	
*/

	double sum = 0;
	if(i < 1 && j > n)
		sum = 0;
	else if(i < 1 && j <= n)
		sum = cost_sum[n] - cost_sum[j-1];
	else if(i >= 1 && j > n)
		sum = cost_sum[i];
	else
		sum = cost_sum[n] - (cost_sum[j-1] - cost_sum[i]);
	return sum*time;	

}

// 计算结果
// record[i][j][0]代表现在在点i, 修复第1,...,i-1,j,,,,n点需要的最少花费
// record[i][j][1]代表现在在点j, 修复第1,...,i,j+1,,,,n点需要的最少花费
double get_min(int i, int j, int k)
{
	if(flag[i][j][k] == c_count)
//	if(flag[i][j][k] == 1)
		return record[i][j][k];
	
	flag[i][j][k] = c_count;
	if(i == 0 && j == n+1)
	{
		record[i][j][k] = 0;
		return record[i][j][k];
	}	

	// 如果待修的点是i-1或j
	if(k == 0)	
	{
		double r1 = -1, r2 = -1;
		if(i-1 >= 1)
			r1 = get_cost(i-1, j, (array[i].x-array[i-1].x)/v);
		if(j <= n)
			r2 = get_cost(i-1, j, (array[j].x-array[i].x)/v);

		double a1 = -1, a2 = -1;
		if(r1 != -1)
			a1 = r1 + array[i-1].cost + get_min(i-1, j, 0);
		if(r2 != -1)
			a2 = r2 + array[j].cost + get_min(i-1, j, 1);
		
		double ans;
		if(a1 == -1 && a2 != -1)		
			ans = a2;
		else if(a1 != -1 && a2 == -1)
			ans = a1;
		else if(a1 != -1 && a2 != -1)
			ans = min(a1, a2);
		else
			ans = 0;	
		record[i][j][k] = ans;
		return record[i][j][k];		
	}	
	// 如果待修的点是i,j+1
	else
	{
		double r1 = -1, r2 = -1;
                if(i >= 1)
                        r1 = get_cost(i, j+1, (array[j].x-array[i].x)/v);
                if(j+1 <= n)
                        r2 = get_cost(i, j+1, (array[j+1].x-array[j].x)/v);

                double a1 = -1, a2 = -1;
                if(r1 != -1)
                        a1 = r1 + array[i].cost + get_min(i, j+1, 0);
                if(r2 != -1)
                        a2 = r2 + array[j+1].cost + get_min(i, j+1, 1);

                double ans;
                if(a1 == -1 && a2 != -1)
                        ans = a2;
                else if(a1 != -1 && a2 == -1)
                        ans = a1;
                else if(a1 != -1 && a2 != -1)
                        ans = min(a1, a2);
		else
			ans = 0;
                record[i][j][k] = ans;
                return record[i][j][k];
	}
}


 
  
这题动态规划不难想,在刚开始,机器人必定是在可能的两侧2个最近的点(如果有的话)选一个地点进行修复。然后再从可能的两侧2个最近的点来修复。
由于题目中n可以达到1000. 于是就想到开这样大的数组,最多为n^2级别。所以构思状态为d(i,j,k).
k = 0时代表已修复了i,i+1,...,j-1,修复剩余的点所花最小花费
k = 1时代表已修复了i+1,...,j,修复剩余的点所花最小花费
(这样的状态表示是不得已而为之)。
这题的要求很死,很容易TLE. 
学习到了:
1. 如果记忆化搜索不能memset, 那么只能用flag标记,并且对每一轮count的序号进行标记。
2. 能算的尽量先算好,比如get_cost函数。
  

你可能感兴趣的:(UVa 1336 Fixing the Great Wall)