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

 先自我介绍一下,本人大一,计算机专业本科在读,大学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从后往前转移即可。

下面是评测记录:

### CCF CSP 货物调度算法实现方案 #### 动态规划的状态定义 对于货物调度问题,动态规划是一种有效的解决方案。为了处理这个问题中的复杂约束条件,状态设计至关重要。一种有效的方法是使用二维数组 `dp[i][j]` 来表示前 i 个仓库,在 j 的成本下可以获得的最大净收入[^3]。 具体来说: - **i** 表示考虑的仓库数量。 - **j** 表示当前累积的成本。 这种状态下,目标是在给定预算内最大化收益。然而,这种方法面临的主要挑战是如何跟踪哪些仓库已经被分配以及它们的具体安排情况。 #### 处理仓库编号重复使用的障碍 为了避免同一个仓库被多错误计数的问题,可以在状态转移的过程中引入额外的信息记录机制。例如,通过位掩码(bitmask)技术来标记已访问过的仓库,从而确保每个仓库仅能参与一交易操作。这样做的好处是可以精确控制资源分配的同时不影响整体的时间性能表现。 ```python from typing import List, Tuple def goods_scheduling(n: int, prices: List[int], costs: List[List[Tuple[int]]]) -> int: """ 计算最优货物调度策略下的最小总费用 参数: n (int): 总共有多少个不同的物品/仓库 prices (List[int]): 各个商品的价格列表 costs (List[List[Tuple[int]]]): 成本矩阵,costs[i] 是一个包含若干二元组(x,y)的列表, 表示从其他地方运送到位置 i 所需支付 y 单位的钱运送 x 数量的商品 返回值: int: 最优解对应的最低总开销 """ INF = float('inf') # 初始化 dp table 和 visited mask dp = [[INF]*(sum(prices)+1) for _ in range(2**n)] dp[0][0] = 0 for state in range(2**n): for next_state in range(state+1, min((state<<1)|1, 2**n)): diff_mask = next_state ^ state if bin(diff_mask).count('1') != 1: continue idx = int(math.log2(next_state & (-next_state))) for cost_item in costs[idx]: quantity, price_per_unit = cost_item new_cost = sum([prices[j]*bin(((diff_mask>>j)&1)) for j in range(len(prices))]) prev_state = next_state ^ (1 << idx) dp[next_state][new_cost] = min( dp[next_state][new_cost], dp[prev_state][price_per_unit*quantity]+new_cost-price_per_unit*quantity ) result = min(dp[-1]) return result if result!=INF else -1 ``` 此代码片段展示了如何利用位运算技巧有效地管理多个仓库之间的关系,并实现了基于上述思路构建起来的一个简化版模型。需要注意的是实际应用中还需要进一步优化内存占用率等问题。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值