Codeforces 119C Education Reform【dp+路径输出】

C. Education Reform
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Yet another education system reform has been carried out in Berland recently. The innovations are as follows:

An academic year now consists of n days. Each day pupils study exactly one of m subjects, besides, each subject is studied for no more than one day. After the lessons of the i-th subject pupils get the home task that contains no less than ai and no more than bi exercises. Besides, each subject has a special attribute, the complexity (ci). A school can make its own timetable, considering the following conditions are satisfied:

  • the timetable should contain the subjects in the order of the complexity's strict increasing;
  • each day, except for the first one, the task should contain either k times more exercises, or more by k compared to the previous day (more formally: let's call the number of home task exercises in the i-th day as xi, then for each i (1 < i ≤ n): either xi = k + xi - 1 or xi = k·xi - 1 must be true);
  • the total number of exercises in all home tasks should be maximal possible.

All limitations are separately set for each school.

It turned out that in many cases ai and bi reach 1016 (however, as the Berland Minister of Education is famous for his love to half-measures, the value of bi - ai doesn't exceed 100). That also happened in the Berland School №256. Nevertheless, you as the school's principal still have to work out the timetable for the next academic year...

Input

The first line contains three integers n, m, k (1 ≤ n ≤ m ≤ 50, 1 ≤ k ≤ 100) which represent the number of days in an academic year, the number of subjects and the k parameter correspondingly. Each of the following m lines contains the description of a subject as three integers ai, bi, ci (1 ≤ ai ≤ bi ≤ 1016, bi - ai ≤ 100, 1 ≤ ci ≤ 100) — two limitations to the number of exercises on the i-th subject and the complexity of the i-th subject, correspondingly. Distinct subjects can have the same complexity. The subjects are numbered with integers from 1 to m.

Please do not use the %lld specificator to read or write 64-bit numbers in С++. It is preferred to use the cin stream or the %I64d specificator.

Output

If no valid solution exists, print the single word "NO" (without the quotes). Otherwise, the first line should contain the word "YES" (without the quotes) and the next n lines should contain any timetable that satisfies all the conditions. The i + 1-th line should contain two positive integers: the number of the subject to study on the i-th day and the number of home task exercises given for this subject. The timetable should contain exactly n subjects.

Examples
Input
4 5 2
1 10 1
1 10 2
1 10 3
1 20 4
1 100 5
Output
YES
2 8
3 10
4 20
5 40
Input
3 4 3
1 3 1
2 4 4
2 3 3
2 2 2
Output
NO

题目大意:

给你M个课程,让你分配出一个N天的作业表。再给你一个常数K,这张作业表需要满足下列条件:

①难度按照递增顺序排布。

②今天布置的作业量需要在a【i】----------b【i】之间,并且今天的作业量是昨天作业量的k倍,或者是+k。

③需要使得总作业量最大。

如果能够分配出来N天的作业表,那么对应输出一个可行解,否则输出NO



思路:


1、考虑dp,设定dp【i】【j】【k】表示第i天,我们分配的任务是j号,并且这一天的作业量为k的最大总作业数,

考虑到k最大可能达到10^15,是一个非常大的内存消耗,根本开不出来。那么观察到:题目保证了a【j】.y-a【j】.x<=100,那么我们能够优化了空间,设定dp【i】【j】【k】表示表示第i天,我们分配的任务是j号,并且这一天的作业量为a【j】.x+k的最大总作业数。这里a【j】.x表示j号作业最少分配的量。


2、那么不难推出其状态转移方程(kk表示题意中的k):

j号作业的难度要高于l号作业的难度:

①dp【i】【j】【k】=max(dp【i】【j】【k】,dp【i-1】【l】【a【j】.x+k-kk-a【l】.x】+a【j】.x+k);

表示今天分配的作业量是昨天分配的作业量+kk的状态转移。

②dp【i】【j】【k】=max(dp【i】【j】【k】,dp【i-1】【l】【(a【j】.x+k)/kk-a【l】.x】+a【j】.x+k);

表示今天分配的作业量是昨天分配的作业量的kk倍的状态转移

③注意一些状态转移过程中的限制条件。


3、那么我们得到了结果之后,回溯求解进行记录即可。

记录过程中注意记录的一定是可行解才行。


4、挺码农的一个题,小心谨慎的去写就行了。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define ll __int64
struct node
{
    ll x,y,c;
    int pos;
}a[150];
ll dp[55][55][125];
ll ans[125][2];
int n,m;
ll kk;
int main()
{
    while(~scanf("%d%d%I64d",&n,&m,&kk))
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<m;i++)
        {
            scanf("%I64d%I64d%I64d",&a[i].x,&a[i].y,&a[i].c);
            a[i].pos=i+1;
        }
        for(int j=0;j<m;j++)
        {
            for(int k=0;k<=a[j].y-a[j].x;k++)
            {
                dp[1][j][k]=a[j].x+k;
            }
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<m;j++)
            {
                for(int k=0;k<=a[j].y-a[j].x;k++)
                {
                    for(int l=0;l<m;l++)
                    {
                        if(l==j)continue;
                        if(a[j].c>a[l].c)
                        {
                            if(a[j].x+k-kk-a[l].x>=0&&a[j].x+k-kk-a[l].x<=a[l].y-a[l].x)
                            {
                                if(dp[i-1][l][a[j].x+k-kk-a[l].x]+a[j].x+k>dp[i][j][k])
                                {
                                    if(dp[i-1][l][a[j].x+k-kk-a[l].x]!=0)
                                    dp[i][j][k]=dp[i-1][l][a[j].x+k-kk-a[l].x]+a[j].x+k;
                                }
                            }
                            if((a[j].x+k)%kk==0&&(a[j].x+k)/kk-a[l].x>=0&&(a[j].x+k)/kk-a[l].x<=a[l].y-a[l].x)
                            {
                                if(dp[i-1][l][(a[j].x+k)/kk-a[l].x]+a[j].x+k>dp[i][j][k])
                                {
                                    if(dp[i-1][l][(a[j].x+k)/kk-a[l].x]!=0)
                                    dp[i][j][k]=dp[i-1][l][(a[j].x+k)/kk-a[l].x]+a[j].x+k;
                                }
                            }
                        }
                    }
                }
            }
        }
        ll output=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<=a[i].y-a[i].x;j++)
            {
                if(output<dp[n][i][j])
                {
                    output=dp[n][i][j];
                }
            }
        }
        if(output==0)printf("NO\n");
        else
        {
            printf("YES\n");
            int cont=0;
            for(int i=n;i>=1;i--)
            {
                int flag=0;
                for(int j=0;j<m;j++)
                {
                    for(int k=0;k<=a[j].y-a[j].x;k++)
                    {
                        if(dp[i][j][k]==output)
                        {
                            if(cont==0)
                            {
                                output-=a[j].x+k;
                                ans[cont][0]=j+1;
                                ans[cont++][1]=a[j].x+k;
                                flag=1;
                                break;
                            }
                            else
                            {
                                ll tmp=a[j].x+k;
                                if(tmp==ans[cont-1][1]-kk||tmp*kk==ans[cont-1][1])
                                {
                                    if(a[ans[cont-1][0]-1].c>a[j].c)
                                    {
                                        output-=a[j].x+k;
                                        ans[cont][0]=j+1;
                                        ans[cont++][1]=a[j].x+k;
                                        flag=1;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if(flag==1)break;
                }
            }
            for(int i=cont-1;i>=0;i--)
            {
                printf("%I64d %I64d\n",ans[i][0],ans[i][1]);
            }
        }
    }
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值