codeforces 119C Education Reform (dp+路径打印)

这是一篇关于codeforces 119C问题的博客,讨论如何安排课程以使得n天的课程难度递增且相邻两天作业量满足特定条件,同时最大化总作业量。博主通过使用动态规划(dp)解决此问题,遇到并解决了排序导致的错误,最终实现正确答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

一个学校要排出n天的课程表,有m个课程,每天有且只能一个课程,给出每个课程需要做的作业量的范围(a[i]——b[i]),和难度。要求n天的课程要按难度递增,并且相邻两天的作业量要满足x[i]=x[i-1]+k或者x[i]=x[i-1]*k。问如何排课程表使得总作业量最大。

题解:

dp[i][j][k]表示到第i天位置,第i天选了j课程,作业量为j+a[i],因为a[i]可以达到10^6,但是b[i]-a[i]不超过100,相当于优化了内存。

接着直接dp就好,需要存下路径,pre[i][j][k] 用结构体,存两个变量一个是选择的课程编号,一个是作业量-a[i]。

这样做数据各种都过,极限数据也过,但是就是wa,四个小时的心酸使。

之后发现排序完序号会乱了,所以加个ID,于是ac了。。。。 

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=1000000007;
#define eps 1e-6
#define maxn 55
#define maxm 105
ll dp[maxn][maxn][maxm];
///前i天,第i天选了第j们课,并且第i天的作业量为k+a[i]时,总作业量的最大值。
struct DP
{
    ll num,exer;
}pre[maxn][maxn][maxm];
struct SUBJ
{
   ll a,b;
   int c;
   int id;
}sub[maxn];
ll num,exer;
int n,m;
ll k;

bool Dp()
{
    memset(dp,-1,sizeof dp);
    memset(pre,-1,sizeof pre);
    for(int j=1;j<=m;j++)
        for(ll x=sub[j].a;x<=sub[j].b;x++)
        {
            dp[1][j][x-sub[j].a]=x;
        }
    for(int i=1;i<n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(ll x=sub[j].a;x<=sub[j].b;x++)
            {
                if(dp[i][j][x-sub[j].a]==-1)continue;
                for(int y=j+1;y<=m;y++)
                {
                    if(sub[y].c<=sub[j].c)continue;
                    ///x表示今天的作业量 x+k表示明天的作业量
                    if(sub[y].a<=x+k&&x+k<=sub[y].b)
                    {
                        if(dp[i+1][y][x+k-sub[y].a]<dp[i][j][x-sub[j].a]+x+k)
                        {
                           dp[i+1][y][x+k-sub[y].a]=dp[i][j][x-sub[j].a]+x+k;
                           pre[i+1][y][x+k-sub[y].a].num=j;
                           pre[i+1][y][x+k-sub[y].a].exer=x-sub[j].a;
                        }
                    }
                    ///x表示今天的作业量 x*k表示明天的作业量
                    if(sub[y].a<=x*k&&x*k<=sub[y].b)
                    {
                        if(dp[i+1][y][x*k-sub[y].a]<dp[i][j][x-sub[j].a]+x*k)
                        {
                            dp[i+1][y][x*k-sub[y].a]=dp[i][j][x-sub[j].a]+x*k;
                            pre[i+1][y][x*k-sub[y].a].num=j;
                            pre[i+1][y][x*k-sub[y].a].exer=x-sub[j].a;
                        }
                    }
                }
            }
        }
    }
    ll ans=-1;
    for(int j=1;j<=m;j++)
    {
        for(ll x=sub[j].a;x<=sub[j].b;x++)
            if(dp[n][j][x-sub[j].a]!=-1&&ans<dp[n][j][x-sub[j].a])
            {
                ans=dp[n][j][x-sub[j].a];
                num=j;
                exer=x-sub[j].a;
            }
    }
    return ans!=-1;
}

void output(int day,ll num,ll exer)
{
    if(pre[day][num][exer].num==-1)
    {
        cout<<sub[num].id<<" "<<exer+sub[num].a<<endl;
        return ;
    }
    output(day-1,pre[day][num][exer].num,pre[day][num][exer].exer);
    cout<<sub[num].id<<" "<<exer+sub[num].a<<endl;
}

bool cmp(SUBJ s1,SUBJ s2)
{
    return s1.c<s2.c;
}

int main()
{
    while(cin>>n>>m>>k)
    {
        for(int i=1;i<=m;i++)
        {
            cin>>sub[i].a>>sub[i].b>>sub[i].c;
            sub[i].id=i;
        }
        sort(sub+1,sub+1+m,cmp);
        if(Dp())
        {
            puts("YES");
            output(n,num,exer);
        }
        else puts("NO");
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值