当前位置:首页 » 《关注互联网》 » 正文

2024-06 第34次 ccf-csp T4 货物调度 100pts 题解

22 人参与  2024年09月22日 12:01  分类 : 《关注互联网》  评论

点击全文阅读


 先自我介绍一下,本人大一,计算机专业本科在读,大学0基础自学算法,无信息竞赛基础,实力低微,这是本人第一次写题解来记录自己做题时的感悟,还是比较紧张的QWQ~

由于实力不济,思路上可能会有很多疏漏,希望各路大神不吝赐教!

一、题目描述

二、思路分析 

 这题是一道比较明显的dp,算法本身是不难确定的,但应如何确立状态函数的两个维度呢?第一想法是用dp[i][j]表示前i个货物在j的代价下能够获取的净收入最大值,但显然行不通,因为在推演过程中无法确定货物对应仓库的编号是否已经使用过,这将是最大的障碍,一时间有点摸不清头脑。

然后我又想到了分组dp再集中处理,但也没有可行思路,反而把问题越想越复杂了。

最后,我转念一想,这题的主角不应该是“货物”,而应该是“仓库”。因为仓库中的货物无论先取哪一个,只要取的数目相同,代价都是一样的,利用这一特性,我们可以弱化“货物”这个概念,就单纯去想每一个仓库要怎么取最优,就好了!所以为了获取最大净收入,我可以直接把货物扔仓库并进行降序排序,这样在每一个仓库中从大到小依次取,找出整体最大值再合并,就是我们要的状态转移了。

三、实现过程和注意事项

1.用一个vector数组a来表示货物价格,a[i][j]表示第i个仓库降序第j的货物的价格;

2.设置状态函数。dp[i][j]表示前i个仓库,以不高于j的费用调度货物,所能达到的净利润最大值;

3.思考状态转移方程。如果不选取第i个仓库,那么dp[i][j]=dp[i-1][j];如果选择k个货物,那么dp[i][j]=dp[i-1][j-b[i]-c[i]*k]+(\sum_{l=1}^{k}a[i][l])-b[i]-c[i]*k.最后将所有范围内的这一转移结果取max即可;

4.注意范围:k<=a[i].size()&&j-b[i]-c[i]*k>=0;

5.初始化dp[0][i]=0.

四、总结

对于dp,大部分像我这种初学者比较困惑的一点就是——维度的含义怎么设置,状态转移方程怎么确立,以及如何初始化之类的问题。通过多练习把这些问题搞懂,结合具体问题具体分析,dp题自然就迎刃而解了。

通过这道题,我们可以获得一些启发——dp没有思维定式,不要想当然地认为就应该去转移货物,殊不知这是命题人的高级障眼法罢了。

五、满分代码

#include<bits/stdc++.h>using namespace std;#define ll long long//csp34-4 货物调度int dp[1005][40010];//前i个仓库费用j可以获得的最大总价值vector<int>a[1005];//第i个仓库的第j个货物的价值int b[1005],c[1005];//仓库属性bool dcmp(int a,int b){    return a>b;}int main(){    int n,m,v;    cin>>n>>m>>v;    for(int i=0;i<=40000;i++)dp[0][i]=0;    for(int i=1;i<=n;i++){        cin>>b[i]>>c[i];    }    for(int i=1;i<=m;i++){        int val,t;        cin>>val>>t;        t++;        a[t].push_back(val);    }    for(int i=1;i<=n;i++){        sort(a[i].begin(),a[i].end(),dcmp);    }    for(int i=1;i<=n;i++){        for(int j=0;j<=40000;j++){            dp[i][j]=dp[i-1][j];//不选i            int sum=0;            for(int k=0;k<a[i].size();k++){//选i                if(b[i]+c[i]*(k+1)>j)break;                sum+=a[i][k];//收益和                dp[i][j]=max(dp[i][j],dp[i-1][j-b[i]-c[i]*(k+1)]+sum-b[i]-c[i]*(k+1));            }        }    }    int ans;    for(int i=0;i<=40000;i++){        if(dp[n][i]>=v){            ans=i;break;        }    }    cout<<ans;    return 0;}

 需要说明的一点是,我没有进行降维处理,因为数据范围不是很大,写出来一次就过了也就没进行修改。也不麻烦,只是要注意j从后往前转移即可。

下面是评测记录:


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/163061.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1