AtCoder Beginner Contest 325题解(D,F,G)

abc325题解

D - Printing Machine

题意:许多件产品进入生产线,第i件产品进入产线时间为 T i T_i Ti,离开时间为 D i D_i Di,每次可以选择产线上的一件产品喷漆,消耗一个单位时间,问最多可以给多少件产品喷漆。

解答:可以看出在当前产线上的产品中,选择离开时间最早的产品喷漆总是最优的。代码实现上有一点难度。

F - Sensor Optimization Dilemma

题意:两种监控探头,每种探头覆盖范围为 L i L_i Li,单价为 C i C_i Ci,数量为 K i K_i Ki,现有N个大小为 D i D_i Di房间需要全覆盖监控(一个房间内放置的监控探头总覆盖范围必须大于等于房间大小),问至少要花多少钱。

解答:其实就是尝试一个房间放多少个,用动态规划实现。令 d p i , j dp_{i,j} dpi,j代表考虑了第1-i房间,用了j个第一种监控探头所花费的最少价钱。那么状态转移方程为 d p i , j = m i n x ≤ ⌈ D i / L 1 ⌉ ( x ∗ C 1 + ⌈ ( D i − x ∗ L 1 ) / L 2 ⌉ ∗ C 2 + d p i − 1 , j − x ) dp_{i,j}=min_{x\leq \lceil D_i/L_1\rceil}(x*C_1+\lceil(D_i-x*L_1)/L_2\rceil*C_2+dp_{i-1,j-x}) dpi,j=minxDi/L1(xC1+⌈(DixL1)/L2C2+dpi1,jx)

代码:

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr i64 inf=0x3f3f3f3f3f3f3f3f;
int main(){
    int n;
    cin>>n;
    vector<int> arr(n);
    for(int i=0;i<n;i++){
        cin>>arr[i];
    }
    vector<int> l(2),c(2),p(2);
    for(int i=0;i<2;i++){
        cin>>l[i]>>c[i]>>p[i];
    }
    vector<vector<i64>> dp(n+1,vector<i64>(p[0]+1,inf));
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        int mx=(1ll*arr[i-1]+l[0]-1)/l[0];
        for(int j=0;j<=p[0];j++){
            for(int k=0;k<=mx&&k<=j;k++){
                int t=(1ll*arr[i-1]-1ll*k*l[0]+l[1]-1)/l[1];
                int pre=(1ll*dp[i-1][j-k]-1ll*(j-k)*c[0])/c[1];
                if(t+pre<=p[1]){
                    dp[i][j]=min(dp[i][j],dp[i-1][j-k]+1ll*k*c[0]+1ll*t*c[1]);
                }
            }
        }
    }
    i64 ans=inf;
    for(int i=0;i<=p[0];i++){
        ans=min(ans,dp[n][i]);
    }
    if(ans==inf){
        cout<<"-1\n";
        return 0;
    }
    cout<<ans<<'\n';
    return 0;
}

G - offence

题意:给定一个字符串S和整数k,每次可以从S中删除"of"后接0-k个字符的子串,问S能被删成最少剩几个字符。

解答:刚开始想的是贪心,即从后往前扫描,遇到o就尽量找f与其结合。这个想法是错的,简单反例: S = o o f o o o f e r f e f S=oofoooferfef S=oofoooferfef k = 2 k=2 k=2

还是要用动态规划。设 d p i , j dp_{i,j} dpi,j为S子串S[i…j]删除操作后最短长度。子串S[i…j],如果第一位是’o’,枚举它和串中哪个’f’结合(必须要可结合,即’o’和’f’之间的子串可被删成空串),若要结合的’f’下标为m,则答案是 m a x ( d p m + 1 , j − k , 0 ) max(dp_{m+1,j}-k,0) max(dpm+1,jk,0)。也可以不和’f’结合,答案就是 1 + d p i + 1 , j 1+dp_{i+1,j} 1+dpi+1,j。如果第一位不是’o’,答案也是 1 + d p i + 1 , j 1+dp_{i+1,j} 1+dpi+1,j。最后 d p i , j dp_{i,j} dpi,j就是取各种情况答案的最小值。按照子串长度递增顺序一层层递推。

代码:

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main(){
    string s;
    int t,n;
    cin>>s>>t;
    n=s.size();
    vector<vector<int>> dp(n+1,vector<int>(n+1,0));
    for(int k=1;k<=n;k++){
        for(int i=0;i<=n-k;i++){
            dp[i][i+k]=1+dp[i+1][i+k];
            if(s[i]!='o'){
                continue;
            }
            for(int j=i+k-1;j>i;j--){
                if(s[j]=='f'&&dp[i+1][j]==0){
                    dp[i][i+k]=min(dp[i][i+k],max(dp[j+1][i+k]-t,0));
                }
            }

        }
    }
    cout<<dp[0][n]<<'\n';
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值