Codeforces Beta Round #90 C题

本文讨论了一个关于在给定课程集合中选择n门课程的问题,要求课程时间在指定范围内,难度递增,且时间差符合特定条件。通过算法优化解决了时间和难度的选择难题,并提供了实现步骤和错误纠正策略。
        真的是好久没有做题了,一点手感都没有,思想也变迟钝了,两战在即,应该慢慢拾回最佳状态了。

       题意:有m门课,每门课有最少花费时间,最多花费时间,难度系数。从这m门课中选出n门,要求每门课所花时间在要求的范围内,且难度是绝对递增的,同时当前一门课所花时间比前一门课所花时间多k或者是它的k倍。问是否可能,YES/NO,若可能的话,输出总时间最多的那种情况(1<=n<=m<=50,1<=k<=100,1<=a,b<10^16且b-a<=100).

       思路:因为a,b范围太大,不能直接保存。但b-a<=100,所以可以开二维表示某门课所要花的时间。最终一定要做n门课,所以再加一维表示当前课程是第几门要做的,dp[i][j][k]表示当前的课程j是选中的第i门课,所花的时间比j的最少时间多k。可以由dp[i-1]推导dp[i],同时又要总时间最多,所以加一个sum[i][j][k]表示第i门课选j的最多的总时间,然后选取最优的情况即可。

       犯的错误:刚开始没有认真看a,b的范围,RE

                         接下来以为总时间最多的情况就是存在比此时时间少k的先选,后选是k倍的(k=1时还要单独考虑一下)。但这只保证了前面一个是最大的,但不能保证前面的和是最大的。在这WA了N久。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

struct point//课程信息
{
	__int64 n,a,b,c;//下标,最少时间,最多时间,难度系数
}p[110];
struct po
{
	__int64 a,b;//上一门课是a,所花时间是p[a].a+b
}pre[51][51][110];
bool cmp(point a,point b)//课程按难度系数排序
{
	if(a.c!=b.c)
		return a.c<b.c;
	if(a.b!=b.b) 
		return a.b>b.b;
	return a.a>b.b;
}
__int64 dp[51][51][110];//dp[i][j][k]表示选的第i门课为j,做p[j].a+k时间
__int64 sum[51][51][110];//选的第i门课为j,做p[j].a+k久的总时间

void DFS(__int64 n,__int64 i,__int64 j)
{
	if(n<=1)
	{
		printf("%I64d %I64d\n",p[i].n,p[i].a+j);
		return;
	}
	DFS(n-1,pre[n][i][j].a,pre[n][i][j].b);
	printf("%I64d %I64d\n",p[i].n,p[i].a+j);
}
int main()
{
	__int64 n,m,k,i,j,kk,ii,j1,a,b,c;
	while(scanf("%I64d%I64d%I64d",&n,&m,&k)!=EOF)
	{
		for(i=0;i<m;i++)
		{
			scanf("%I64d%I64d%I64d",&p[i].a,&p[i].b,&p[i].c);
			p[i].n=i+1;
		}
		
		memset(dp,0,sizeof(dp));
		memset(sum,0,sizeof(sum));
		sort(p,p+m,cmp);//按难易排序
		for(kk=1;kk<=n;kk++)
			for(i=0;i<m;i++)
			{
				for(j=0;j<=p[i].b-p[i].a;j++)
				{
					if(kk==1)
					{
						dp[kk][i][j]=p[i].c;
						sum[kk][i][j]=p[i].a+j;
						continue;
					}

					for(a=0,b=0,c=0,ii=0;ii<i;ii++)//如果上一门课选ii
					{
						if(p[ii].c==p[i].c) break;
						if(p[i].a+j-k>=p[ii].a&&p[i].a+j-k<=p[ii].b&&dp[kk-1][ii][p[i].a+j-k-p[ii].a])//比上一门课多k
						{
							j1=p[i].a+j-k-p[ii].a;
							if(sum[kk-1][ii][j1]>c)
								a=ii,b=j1,c=sum[kk-1][ii][j1];
						}
						
						if((p[i].a+j)%k==0&&(p[i].a+j)/k>=p[ii].a&&(p[i].a+j)/k<=p[ii].b&&dp[kk-1][ii][(p[i].a+j)/k-p[ii].a])//是上一门课的k倍
						{
							j1=(p[i].a+j)/k-p[ii].a;
							if(sum[kk-1][ii][j1]>c)
								a=ii,b=j1,c=sum[kk-1][ii][j1];
						}

						if(c)//如果可以
						{
							dp[kk][i][j]=p[i].c;
							pre[kk][i][j].a=a;
							pre[kk][i][j].b=b;
							sum[kk][i][j]=c+p[i].a+j;
						}
					}
				}
		}

		for(a=0,b=0,c=0,i=0;i<m;i++)//找总时间最多的
			for(j=0;p[i].a+j<=p[i].b;j++)
				if(dp[n][i][j])
				{
					if(sum[n][i][j]>c) 
						a=i,b=j,c=sum[n][i][j];
				}

		if(!c)
		{
			printf("NO\n");
			continue;
		}
		printf("YES\n");
		DFS(n,a,b);//深搜输出
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值