今天学学动态规划

Returning Home 和 WorkConversion

昨晚回去的路上理了一下Dijkstra算法,都是求最短路径,但是我对别的算法实在是一点也想不起来,比如佛洛依德和Prim。因此在最短时间内能达到最好的效果,就还是用Dijkstra。

先说说 Returning Home:

The farmer wantsto return home, Farm No. 1 from Farm No. N. The farmer who is very tired isgoing to return home as fast as he can.

In the farms hecultivates, there is the total number of T of two-way roads. Create a programthat helps the tired farmer find the shortest distance from Farm No. N to FarmNo. 1 Home.

Input

1

6 5

1 2 20

2 3 30

3 4 20

4 5 20

1 5 100

5 150

Output

Case #1

50

Dijkstra的基本原理是:每次找到离源点最近的一个点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。

int Djs() {
	int i,j;
	int u = 1,v = 1;
	mark[1] = 1;
	for(i=1; i<=N-1; i++) {  // 第一个点到其它点的最短距离,因此遍历到N-1就足够了
		// To find the min
		int min_value = inf;   // 先将最小值赋予无穷大
		for(j=1; j<=N; j++)    // 理论上这道题可以从j=2开始,因为就走一次Djs(),并且mark[1]本来就等于1,不过为了算法的通用性,我们还是从1开始遍历。
		{
			if(dis[j]<min_value && mark[j] == 0) {   // 本循环为了找到不确定点中与第一个点距离最短的点
				min_value = dis[j];
				u = j;  // u就是距离最短的点的位置
			}
		}
		mark[u] = 1;  // 不可能有别的路径比直接从1点到该点更短了,因为1点到其它点都比到该点的距离要长,因此到该点的最短路径已确定。

		for(v=1; v<=N; v++) {   // 本循环为了找到1点通过u点能到的点
			if(map[u][v]<inf) {  // 如果该v点是可到达的
				if(dis[v] > dis[u]+map[u][v]) {   //  并且通过u点(1-u-v)到达能够比从1点(1-v)直接到达的距离要更短
					dis[v] = dis[u]+map[u][v];   // 则更新dis,永远保留1点能到达的最短路径
				}
			}
		}
	}
	return dis[N];  // 返回1点到N点的最短路径
}

        好好理解Djsktra用来求源点到其它点的最低距离的原理,基本上这样的题就差不多了。但是我还是很二地纠结了许久,总是只对一半的case,才惊觉我完全忘了考虑这是无向图。

for(i=0; i<R; i++) {
	cin>>S;
	cin>>E;
	cin>>D;
	map[S][E] = GetMin(map[S][E],D);
	map[E][S] = GetMin(map[E][S],D);   // Important! >_<
}
也就是说1 –> 5 100,5->1 50这样的case在无向图的概念里1->5 最短路径是50, 5->1的最短路径也得是50。

贴一下全部的代码吧。

#include <iostream>
using namespace std;
#define MAX 1002
#define inf 9999999
#define GetMin(A,B) (A<B)?(A):(B)
//#define TEST

int map[MAX][MAX];
int R, N;
int dis[MAX]; // 存储1点到其余各点距离
int mark[MAX]; // 已确定最短距离的点的标志数组
int Answer;

void printDis() {
	cout<<"----Dis-----------"<<endl;
	int i;
	for(i=1;i<=N;i++) {
		if(dis[i] == inf) cout<<"*"<<" ";
		else {
		cout<<dis[i]<<" ";
		}
	}
	cout<<endl;
}

void printMap() {
	cout<<"----Map-----------"<<endl;
	int i,j;
	for(i=1;i<=N;i++) {
		for(j=1; j<=N; j++){
			if(map[i][j] == inf) cout<<"*"<<" ";
			else {
				cout<<map[i][j]<<" ";
			}
		}
		cout<<endl;
	}
}

int Djs() {
	int i,j;
	int u = 1,v = 1;
	mark[1] = 1;
	for(i=1; i<=N-1; i++) {  // 第一个点到其它点的最短距离,因此遍历到N-1就足够了
		// To find the min
		int min_value = inf;   // 先将最小值赋予无穷大
		for(j=1; j<=N; j++)    // 理论上这道题可以从j=2开始,因为就走一次Djs(),并且mark[1]本来就等于1,不过为了算法的通用性,我们还是从1开始遍历。
		{
			if(dis[j]<min_value && mark[j] == 0) {   // 本循环为了找到不确定点中与第一个点距离最短的点
				min_value = dis[j];
				u = j;  // u就是距离最短的点的位置
			}
		}
		mark[u] = 1;  // 不可能有别的路径比直接从1点到该点更短了,因为1点到其它点都比到该点的距离要长,因此到该点的最短路径已确定。

		for(v=1; v<=N; v++) {   // 本循环为了找到1点通过u点能到的点
			if(map[u][v]<inf) {  // 如果该v点是可到达的
				if(dis[v] > dis[u]+map[u][v]) {   //  并且通过u点(1-u-v)到达能够比从1点(1-v)直接到达的距离要更短
					dis[v] = dis[u]+map[u][v];   // 则更新dis,永远保留1点能到达的最短路径
				}
			}
		}
#ifdef TEST
		cout<<"mark["<<u<<"]=1"<<endl;
#endif
	}
	return dis[N];  // 返回1点到N点的最短路径
}

int main(int argc, char** argv)
{
	int T, test_case;
	int i,j;
	int S,E,D;

	freopen("input.txt", "r", stdin);

	cin >> T;
	for(test_case = 0; test_case  < T; test_case++)
	{
		cin>>R;
		cin>>N;
		Answer = 0;

		for(i=1; i<=N; i++) {
			for(j=1; j<=N; j++) {
				if(i==j) {
					map[i][j] = 0;
				} else {
					map[i][j] = inf;
				}
			}
			mark[i] = 0;
		}

		for(i=0; i<R; i++) {
			cin>>S;
			cin>>E;
			cin>>D;
			map[S][E] = GetMin(map[S][E],D);
			map[E][S] = GetMin(map[E][S],D);   // Important >_<! 因为本题是无向图。 否则永远是75/150,还是在运气好的情况。
		}
		for(i=1;i<=N;i++) {
			dis[i] = map[1][i];   // dis是用来存储1点到其余各点距离的数组,因此,初始化为map[1][i]。
		}
#ifdef TEST
		printMap();
		printDis();
#endif
		
		Answer = Djs();

#ifdef TEST
		printDis();
#endif

		// Print the answer to standard output(screen).
		cout << "Case #" << test_case+1 << endl;
		cout << Answer << endl;
	}

	return 0;//Your program should return 0 on normal termination.
}

接下来说说Work conversion。

Most work in a factory is performed byrobots. In the robots’ work, the most time used is when converting anoperation.

Therefore, if you design an automationsystem, you need to consider the work conversion.

Consider the work conversion status asgiven in the graph below:

or the above case,

it is 1, 1, 2 for the shortest frequency ofwork conversion from Work No. 1 to Work No. 2, 3 and 4;

it is 3, 2, 1 from Work No. 2 to No. 1, 3and 4; it is 1, 2, 3 for Work No. 3 to No. 1, 2 and 4;

and it is 2, 3, 1 from Work No. 4 to No. 1,2 and 3.

At this moment, the sum of the shortestfrequencies of the work conversions is 1+1+2+3+2+1+1+2+3+2+3+1 = 22.

In this graph, all pairs are 12 pairs i.e.,from Work No. 1 to No. 2; from No. 1 to No. 3; ...; from No. 4 to No. 3.

Therefore, the mean frequency of conversionfrom a certain work to another is 22/12, which is 1.833.

Calculate the mean frequency of workconversions from a certain work to another when a graph of the work conversionstatus is given.

 

         这个题和returninghome区别在于它是个有向图,并且需要知道以每个点为源点到其它点的最短路径。这不难。

	for(i=1; i<=city_num; i++) {  // 相当于每个点都Djskstra一遍
		Djs(i);
	}

而鄙人再次犯了一个极其愚蠢的错误。故事是这样的。

对于这样的题,通常map[MAX][MAX]数组的0行0列都默认抛弃了,所以我在初始化的时候就默默地一并抛弃了,写了个i从1 to MAX遍历[for(i=1;i<=MAX;i++)]。你猜怎么着,呵呵,果断越界了,但是能编译过,VS也运行OK,所以没发现错误。一到提交就是Wrong answer。

       for(i=0;i<MAX;i++) { 
        for(j=0;j<MAX;j++) {
            if(i == j) {
                map[i][j] = 0;
                dis[i][j] = 0;
            }else {
                map[i][j] = inf;
                dis[i][j] = inf;
            }
            mark[i][j] = 0;   // 初始化很重要
        }
    }

另外,本题最大的意义在于,让我们回忆一下怎么输出小数。

要求的结果是保留三位小数。

Output

Case #1

1.833

Case #2

2.255

用C语言的话,

printf("%0.3f\n",Answer); 就可以输出正确了。

 

C++呢?有两种。

第一种是cout <<fixed<<setprecision(3)<< Answer <<endl;

第二种是

cout.precision(3);

cout<<fixed<<Answer<<endl;

其中,第一种方法要求#include<iomanip>,第二种方法可以直接使用。

这里面最重要的是fixed。否则你永远对不了所有的case,没错,就是这么心酸。

#include<iostream>
#include<iomanip>
using namespace std;

int main() {
	float pi = 3.1415;
	cout<<"set precision 3:  "<<setprecision(3)<<pi<<endl;
	cout<<"set precision 6:  "<<setprecision(6)<<pi<<endl; // 后面不补0
	
	// 需要注意的是:一旦在语句中加入了格式控制fixed,那么在后面的cout语句都默认按照前面规定的fixed格式输出,除非显示地改变格式控制
	cout<<"set fixed precision 6:  "<<fixed<<setprecision(6)<<pi<<endl;
	cout<<"after fixed:  "<<setprecision(6)<<pi<<endl;

	// 显示地修改格式控制
	cout<<fixed<<setprecision(3)<<pi<<endl;

	// 使用scientific也要和fixed一样注意
	cout<<scientific<<setprecision(3)<<pi<<endl;
	cout<<setprecision(3)<<pi<<endl;
}

输出


int main() {
	cout.precision(3);
	cout<<pi<<endl;
	cout<<fixed<<pi<<endl;
	cout<<pi<<endl;
	cout.unsetf( ios::fixed ); 
	cout<<pi<<endl;
}

输出:



Ok,到这就说得差不多了。

关于小数输出,参考大神的http://www.cnblogs.com/krisdy/archive/2009/04/17/1438402.html,里面写得比较详细~





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值