51nod-1636 教育改革

题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题
 收藏
 关注

最近A学校正在实施教育改革。

一个学年由n天组成。A学校有m门课程,每天学生必须学习一门课,一门课程必须在一天内学习完。在学习完第i门课程后,学生们会收到 xi 个家庭作业,其中 xi[ai,bi] 。每门课还有一个属性,就是复杂度 ci 。A学校现在要制他们的课程表,具体要求如下:

·在课程表中,随着天数的增加,课程的复杂度是严格递增的。

·除了第1天,每天的作业量必须是前一天的k倍,或者比前一天多k个作业。(假设第i天的作业量为 xi ,则对于i(1<i≤n)到满足 xi  k+xi1 或 xi  kxi1 );

现在,给定天数n,系数k,和m门课程的ai,bi,ci(1≤i≤m)。要求计算一个学年可以安排最大的总作业量( ni=1xi )是多少。


Input
单组测试数据
第一行,三个由空格隔开的整数n,m,k(1≤n≤m≤50,1≤k≤100),表示一个学年的天数,课程的数量,和作业增量系数。
接下来的m行,
每行有三个整数,ai,bi,ci(1≤ai≤bi≤10^16,bi-ai≤100,1≤ci≤100)
分别表示第i门课程的最小作业量,和最多作业量,以及复杂度。
不同的课程可以有相同的复杂度。课程编号从1到m。
Output
如果有可行方案,第一行输出“YES”(没有引号),第二行输出最大的作业量。
如果没有可行方案,则输出一行“NO”(没有引号)。
Input示例
4 5 2
1 10 1
1 10 2
1 10 3
1 20 4
1 100 5
Output示例
YES
78

题解:动态规划一般步骤:分解子问题,得到转移方程。在本题中,首先按照复杂度c进行升序排序。对于i位置,我们定义dp[i]为0 ~ i - 1能到达i位置的容器(即满足p[i] > p[j] && j位置有元素 + k或者*k后范围在p[i].b ~ p[i].b之间)很明显i是能到达i的(即以i位置的左右区间元素为开始元素)对于能满足+k或者*k在p[i].a ~ p[i].b区间的元素,将新元素加入容器中,并且更新dp[i]的num值(+1)。当num值等于n时,表示能够满足n天的课程,将ok变成1(ok初始为0,最终为0则是NO,反之为YES)

接下来的问题是怎么获得最大值,我们发现,当num等于n时,当前放入容器的新元素的最大值是不受任何约束的(即它取最大值不受之前范围的影响),而在它取最大值时,约束了num等于n - 1时的取值范围,在范围内去任何元素都是不受约束的,基于此我们从最大值开始逆推,当当前维护的最大值元素 - k1 或者 /k1在num - 1 元素的范围内时,取较大值,反之,哪个不符合取另外一个(不可能两个都不符合)

AC代码

#include <stdio.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cmath>
 
using namespace std;

typedef long long ll;

const int maxn = 111;
struct node{
	ll a, b, c;
}p[maxn];

struct node1{
	ll num, prex, prey;
	node1(ll num_, ll x_, ll y_):num(num_), prex(x_), prey(y_){}
};

vector<node1> dp[maxn];

vector<ll> a[maxn][10 * maxn];

bool cmp(node a1, node a2){
	return a1.c < a2.c;
}

bool check(ll l, ll r, ll a, ll b){
	if(a > r || b < l)
		return false;
	return true;
}

int main(){
//	freopen("in.txt", "r", stdin);
	ll n, m, k1;
	scanf("%lld%lld%lld", &n, &m, &k1);
	for(ll i = 0; i < m; i++)
		scanf("%lld%lld%lld", &p[i].a, &p[i].b, &p[i].c);
	sort(p, p + m, cmp);
	ll ok = 0;
	ll ans = 0;
	if(n == 1){
		for(ll i = 0; i < m; i++)
			ans = max(ans, p[i].b);
		printf("YES\n");
		printf("%lld\n", ans);
		return 0;
	}
	for(ll i = 0; i < m; i++){
		if(m - i >= n){
			dp[i].push_back(node1(1, -1, -1));
			for(ll j = p[i].a; j <= p[i].b; j++)
				a[i][0].push_back(j);
		}
		for(ll j = 0; j < i; j++){
			ll len = dp[j].size();
			for(ll k = 0; k < len; k++){
				map<ll, ll> s;
				if(a[j][k].size() == 0)
					continue;
				if(p[i].c <= p[j].c)
					continue;
				ll len1 = a[j][k].size();
				sort(a[j][k].begin(), a[j][k].end());
				if(!check(min(a[j][k][0] + k1, a[j][k][0] * k1), max(a[j][k][len1 - 1] + k1, a[j][k][len1 - 1] * k1), p[i].a, p[i].b))
					continue;
				if(m - i + dp[j][k].num >= n){
					for(ll l = 0; l < len1; l++){
						ll cc = a[j][k][l] + k1;
						if(cc >= p[i].a && cc <= p[i].b && !s[cc]){
							a[i][dp[i].size()].push_back(cc);
							 s[cc] = 1;
						}
						cc = a[j][k][l] * k1;
						if(cc >= p[i].a && cc <= p[i].b && !s[cc]){
							a[i][dp[i].size()].push_back(cc);
							 s[cc] = 1;
						}			
					}
					if(a[i][dp[i].size()].size() > 0){
						if(dp[j][k].num < n - 1)
							dp[i].push_back(node1(dp[j][k].num + 1, j, k));
						else{
							ok = 1;
							sort(a[i][dp[i].size()].begin(), a[i][dp[i].size()].end());
							ll t_ans = a[i][dp[i].size()][a[i][dp[i].size()].size() - 1];
							ll sum = t_ans;
							ll x = j;
							ll y = k;
							while(x + y != -2){
								sort(a[x][y].begin(), a[x][y].end());
								ll l = a[x][y][0];
								ll r = a[x][y][a[x][y].size() - 1];
								if((t_ans - k1) > r || t_ans - k1 < l){
									sum += t_ans / k1;
									t_ans = t_ans / k1;
								}
								else if(t_ans / k1 < l || t_ans / k1 > r){
									sum += t_ans - k1;
									t_ans = t_ans - k1;
								}
								else{
									sum += max(t_ans - k1, t_ans / k1);
									t_ans = max(t_ans - k1, t_ans / k1);
								}
								l = dp[x][y].prex;
								r = dp[x][y].prey;
								x = l;
								y = r;
							}
							if(sum > ans)
								ans = sum;
							a[i][dp[i].size()].clear();
						}
					}
				}
			}
		}
	}
	if(ok){
		printf("YES\n");
		printf("%lld\n", ans);
	}
	else
		printf("NO\n");
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值