CF背包DP

题目1

题目链接:Cut Ribbon - CodeForces 189A - Virtual Judge

思路:二进制优化背包问题,二进制拆分,然后跑01背包即可

代码如下:

#include <bits/stdc++.h>

using i64 = long long;
using namespace std;

const int mod = 1e9 + 7;
const int MAXN = 100005; 
void solve() {
    int n;
    cin>>n;
    vector<int>a(3);
    for(int i=0; i<3; i++)cin>>a[i];
    vector<int>ve[3];
    for(int i=0; i<3; i++){
        int maxnum=n/a[i];
        int j=0;
        for( j=1; j<=maxnum; j*=2){
            ve[i].push_back(j);
        }
        maxnum=maxnum-j;
        if(maxnum>0)ve[i].push_back(maxnum);
    }
    vector<int>f(n+1,-MAXN);
    f[0]=0;
    for(int i=0; i<3; i++){
        for(auto v:ve[i]){
            for(int j=n; j>=a[i]*v; j--){
                f[j]=max(f[j],f[j-a[i]*v]+v);
            }
        }
    }
    cout<<f[n]<<endl;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    solve();
    
    return 0;
}

题目2

题目链接:Marvolo Gaunt's Ring - CodeForces 855B - Virtual Judge

思路:我们考虑那么一个状态dp[i][j]表示第i个价格,在前j个物品的最大价值。所以答案为dp[3][n]

代码如下:

#include <bits/stdc++.h>

using i64 = long long;
using namespace std;

const int mod = 1e9 + 7;
const i64 INF =9*1e18; 
void solve() {
    int n;
    cin>>n;
    vector<i64>b(3),a(n+1);
    for(int i=0; i<3; i++)cin>>b[i];
    for(int i=0; i<n; i++)cin>>a[i];
    vector<vector<i64>>f(4,vector<i64>(n+1,0));
    i64 ans=0;
    for(int i=0; i<3; i++){
        i64 maxnum=-INF;
        for(int j=0; j<n; j++){
            f[i+1][j]=max(f[i][j]+a[j]*b[i],maxnum);
            maxnum=f[i+1][j];
        }
        ans=maxnum;
    }
    cout<<ans<<endl;
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    solve();
    
    return 0;
}

题目3

题目链接:https://vjudge.net/problem/CodeForces-366C

思路:

我们考虑a/b=k转化为a-k*b=0;因此我们求出a-k*b的最大价值和即可

代码如下:

#include <bits/stdc++.h>

using i64 = long long;
using namespace std;

const int mod = 1e9 + 7;
const i64 INF =9*1e18; 
void solve() {
    int n,k;
    cin>>n>>k;
    vector<int>a(n),b(n);
    vector<vector<int>>dp(101,vector<int>(20010,-1));
    dp[0][10000]=0;
    for(int i=0; i<n; i++){
        cin>>a[i];
    }for(int i=0; i<n; i++)cin>>b[i];
    for(int i=1; i<=n;i++){
        for(int j=0; j<=20000; j++){
            if(dp[i-1][j]==-1)continue;
            int y=j+a[i-1]-b[i-1]*k;
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
            if(y>=0 && y<=20000){
                dp[i][y]=max(dp[i][y],dp[i-1][y]);
                dp[i][y]=max(dp[i][y],dp[i-1][j]+a[i-1]);
            }
        }
    }
    cout<<((dp[n][10000]==0)?(-1):(dp[n][10000]))<<endl;
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    solve();
    
    return 0;
}

题目4

题目链接:https://vjudge.net/problem/CodeForces-788C

思路:我们考虑题目三的转化,s1+s2+s3=3*n,看出s1+s2-2*n+s3-n=0,即s1+s2-n=x;x+s3-n=n;

考虑出递推公式后我们便很快的做出来,我们考虑BFS,求出每个值的第一次出现;

代码如下:

#include <bits/stdc++.h>

const int N = 1000;
using namespace std;
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(k);
    for (int i = 0; i < k; ++i) {
        std::cin >> a[i];
    }
    std::sort(a.begin(), a.end());
    a.erase(std::unique(a.begin(), a.end()), a.end());
    
    if (n < a[0] || n > a.back()) {
        std::cout << -1 << "\n";
        return 0;
    }
    
    std::vector<int> dis(N + 1, -1);
    std::queue<int> que;
    k=a.size();
    for (int i = 0; i < k; ++i) {
        if (a[i] == n) {
            std::cout << 1 << "\n";
            return 0;
        }
        dis[a[i]] = 1;
        que.push(a[i]);
    }
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        for (auto x : a) {
            int v = u + x - n;
            if (0 <= v && v <= N && dis[v] == -1) {
                dis[v] = dis[u] + 1;
                que.push(v);
            }
        }
    }
    cout << dis[n] << "\n";
    
    return 0;
}

题目5

题目链接:https://vjudge.net/problem/CodeForces-118D

思路:为了区分n1以及n2,我们考虑dp[n1][n2][2]代表,i个步兵,j个骑兵,结尾为01(步兵骑兵)的方案数;

转移方程为:dp[i][j][1]+=dp[i][j-k][1];

代码如下:

#include <bits/stdc++.h>

using i64 = long long;
using namespace std;

const int mod = 1e8;
const i64 INF =9*1e18; 
int dp1[110][110][2];

void solve() {
    int n1,n2,k1,k2;
    cin>>n1>>n2>>k1>>k2;
    
    dp1[0][0][0]=dp1[0][0][1]=1;
    for(int i=0; i<=n1; i++){
        for(int j=0; j<=n2; j++){
            for(int k=1; k<=k1 && k<=i; k++){
                dp1[i][j][0]+=dp1[i-k][j][1];
                dp1[i][j][0]%=mod;
            }
            for(int k=1; k<=k2 && k<=j; k++){
                dp1[i][j][1]+=dp1[i][j-k][0];
                dp1[i][j][1]%=mod;
            }
        }
    }
    int ans=0;
    ans=(dp1[n1][n2][0]+dp1[n1][n2][1])%mod;
   cout<<ans<<endl;
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    
    solve();
    
    return 0;
}

题目6

题目链接:https://vjudge.net/problem/CodeForces-922E

思路:这个实际上是贪心+dp,我们考虑击杀k个恶魔,法力值剩余最大,因为法力值上限只和击杀恶魔数量有关。

代码如下:

#include <bits/stdc++.h>

using i64 = long long;
using namespace std;

const int mod = 1e8;
const i64 INF =9*1e18; 
pair<i64,i64> dp[1005][10005];

void solve() {
    int n,W,B,X;
    cin>>n>>W>>B>>X;
    vector<i64>c(n+1);
    i64 sum=0;
    for(int i=1; i<=n; i++){
        cin>>c[i];
        sum+=c[i];
    }
    vector<i64>cost(n+1);
    for(int i=1; i<=n; i++)cin>>cost[i];
    memset(dp,-0x3f,sizeof dp);
    dp[0][0]={W,W};
    for(int i=1; i<=n; i++){
        for(i64 j=0; j<=sum; j++)
        dp[i-1][j].first=min(dp[i-1][j].first+X,dp[i-1][j].second);
        for(i64 v=0; v<=c[i]; v++){
            for(i64 j=0; j<=sum; j++){
                //cout<<i<<' '<<j<<' '<<dp[i][j].first<<' '<<dp[i][j].second<<endl;
                if(dp[i][j].first<dp[i-1][j].first){
                    dp[i][j]=dp[i-1][j];
                }else if(dp[i][j].second<dp[i-1][j].second){
                    dp[i][j]=dp[i-1][j];
                }
                if(j>=v){
                    if(dp[i-1][j-v].first-cost[i]*v>=0 && dp[i][j].first<dp[i-1][j-v].first-cost[i]*v){
                        dp[i][j].first=dp[i-1][j-v].first-cost[i]*v;
                        dp[i][j].second=dp[i-1][j-v].second+B*v;
                    }
                }
                 //cout<<i<<' '<<j<<' '<<dp[i][j].first<<' '<<dp[i][j].second<<endl;
            }
        }
    }
    for(i64 i=sum; i>=0; i--){
        if(dp[n][i].first>=0){
            cout<<i<<endl;
            return ;
        }
    }
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    
    solve();
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值