诡异的电梯【Ⅰ】

诡异的电梯【Ⅰ】

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 3
描述

新的宿舍楼有 N(1≤N≤100000)  and M(1≤M≤100000)个学生在新的宿舍楼里为了节约学生的时间也为了鼓励学生锻炼身体所以规定该宿舍楼里的电梯在相邻的两层之间是不会连续停下(即,如果在第2层停下就不能在第3层停下。).所以,如果有学生在相邻的两层之间要停下则其中的一部分学生必须选择走楼梯来代替。规定:一个人走下一层楼梯的花费为A,走上一层楼梯的花费为B。(1≤A,B≤100)现在请你设计一个算法来计算出所有学生走楼梯花费的最小费用总和。 所有的学生一开始都在第一层,电梯不能往下走,在第二层的时候电梯可以停止。


输入
输入有几组数据T。T(1≤T≤10)
每组数据有N (1≤N≤100000),M(1≤M≤100000),A,B(1≤A,B≤100)。
接下来有M个数字表示每个学生想要停的楼层。

输出
输出看样例。
样例输入
1
3 2 1 1
2 3
样例输出
Case 1: 1
 
   
思路:
   

这其实就是一个简单的一维dp,用dp【i】表示从1层上到第 i 层花费的最小的体力。

因为不能在相邻的楼层停留,所以可以从dp【i-2】转移,但这样不是最优的还要从dp【i-3】转移,因为这样的话就可以到达所有的楼层。我们只要在所有的之间dp最优即可。

其他要注意的一个条件是,从dp【i-3】转移时,中间两层的人有四种选择:

1:都上去或都下来

2:上面的上去一层,下面的下来一层

3:上面的下来两层,下面的上去两层,(当时没有考虑到这个,要细心啊)

题目讲解:题目的状态转移方程是比较难想,但是画一下图就能发现转移方程;
下面图解now代表当前的楼层:到达每一个楼层,共有下面四种情况;
 存在bug,
3 2 1 1
2 2
时输出结果为2,保留以后改进
我的代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
	int T,n,m,up,down,i,k,t,cnt,mi;
	int a[100010],dp[100010];
	cin >> T;
	for(cnt=1;cnt<=T;++cnt)
	{
		memset(a,0,sizeof(a));
		cin >> n >> m >> down >> up;
		for (i=1;i<=m;++i)
		{
			cin >> t;
			a[t]++;
		}
		memset(dp,0x3f3f3f3f,sizeof(dp));
		dp[0]=dp[1]=dp[2]=0;
		mi=min(up,down);
		for (i=3;i<=n;++i)
		{
			dp[i]=min(dp[i],dp[i-2]+a[i-1]*mi);
		/*因为以3层楼里找最优,因为它是相隔一层楼的,且最顶楼的不考虑上面楼层,
		所以当现在的最顶层变最低层时,就可以考虑中间的楼层是上或下楼了*/
			if(i-3>=1)
			{
				k=min((a[i-2]*2+a[i-1])*up,(a[i-2]+a[i-1]*2)*down);
				k=min(k,a[i-1]*up+a[i-2]*down);
				k=min(k,a[i-1]*down*2+a[i-2]*up*2);
				dp[i]=min(dp[i],dp[i-3]+k);
			}
		}
		cout <<"Case " << cnt << ": " << dp[n] << endl;
	}
	return 0;
}

标程:
 
   
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int dp[100005];
int flo[100005];
int main()
{
	int kase = 0;
	int T;
	scanf("%d", &T);
	while (T--)
	{
		memset(dp, 0, sizeof(dp));
		memset(flo, 0, sizeof(flo));
		int n, m, a, b;
		scanf("%d %d %d %d", &n, &m, &a, &b);
		for (int i = 0; i < m; i++)
		{
			int t;
			scanf("%d", &t);
			flo[t]++;
		}

		int minn;
		for (int i = 3; i <= n; i++)
		{
			minn = min(flo[i - 1] * a, flo[i - 1] * b) + dp[i - 2];
			//printf("minn = %d\n", minn);
			int t1 = min(flo[i - 1] * a + flo[i - 2] * a, flo[i - 1] * b + flo[i - 2] * b);
			int t2 = min(flo[i - 1] * b + flo[i - 2] * a, flo[i - 1] * a + flo[i - 2] * b);
			int t3 = min(t1, t2) + dp[i - 3];
			//printf("t1 = %d, t2 = %d, t3 = %d\n", t1, t2, t3);
			minn = min(minn, t3);
			//printf("minn = %d\n", minn);
			dp[i] = minn;
		}
		printf("Case %d: %d\n", ++kase, dp[n]);
	}

	return 0;
}        


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值