链接:NYOJ:click here POJ:click here
题意:
在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。
思路:
贪心思想(一般都是先排序)
每次从此岸到对岸移动的两个人要么这两个人中有一个是时间最快的那个人,要么这两个人到达对岸后再也不回来。即:要么最快+最慢(最快回来换人),要么最慢+次慢(不回来)。
1.对N个人过河时间从小到大排序。cost[i];
2.分情况讨论:
⑴当n = 1,直接过河。sum = cost[0]
(2)当n = 2,直接过河。 sum = cost[1]
(3)当n = 3,无论怎么过河, sum = cost[0] + cost[1] + cost[2]
(4)当n = 4,设从小到大排序后位a=cost[0],b=cost[1],c=cost[2],d=cost[3];
用最小的来送:b + a + c + a + d =2*a+b+c+d= 2*cost[0] + cost[1] + cost[2] + cost[3](a,b过去,a回来,a,c过去,a回来,a,d过去)两小送两大:b + a + d + b + b =a+3*b+d= cost[0] + 3*cost[1] + cost[3](a,b过去,a回来,c,d过去,b回来,a,b过去)
所以sum = min(2a + b + c + d, a + 3b + d)
(5)当n > 4,设从小到大排序后位a,b,……,c,d,大于4个人,a,b是最小的两个人,c,d是最大的两个人,目标就是把最大的两个人送过去。就要牺牲最小的。
用最小的来送:A=d + a + c + a = 2a + c + d=2*cost[0]+cost[2]+cost[3](a,d过去,a回来,a,c过去,a回来)两小送两大:B= b + a + d + b = a + 2b + d=cost[0]+2*cost[1]+cost[3];(a,b过去,a回来,c,d过去,b回来)
循环:sum = min(A,B),直到n <= 4时候结束。
思路理解清晰,代码不难实现:
//NYOJ 47 过河问题: //POJ 1007 #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; int cost[1010]; int main() { //freopen("1.txt","r",stdin); //freopen("2.txt","w",stdout); int ncase,m,n,i,j; int sum; scanf("%d",&ncase); while(ncase--) { sum=0; scanf("%d",&m); memset(cost,0,sizeof(cost)); for(i=0; i<m; i++) scanf("%d",&cost[i]); sort(cost,cost+m); while(m) { if(m==1) { sum+=cost[0]; break; } else if(m==2) { sum+=cost[1]; break; } else if(m==3) { sum+=(cost[0]+cost[1]+cost[2]); break; } else if(m==4) { if(m==4) sum +=min(2*cost[0]+cost[1]+cost[2]+cost[3],cost[0]+3*cost[1]+cost[3]); break; } else { //if(cost[m-2]-2*cost[1]+cost[0]<=0) //sum+=min(cost[m-1]+cost[m-2]+2*cost[0],) sum+=min(cost[m-1]+cost[m-2]+2*cost[0],cost[m-1]+2*cost[1]+cost[0]); m-=2; } } printf("%d\n",sum); } return 0; }