首页 > 其他分享 >CodeForces - 118D - dp

CodeForces - 118D - dp

时间:2024-10-03 09:45:05浏览次数:1  
标签:取用 ll CodeForces k2 k1 步兵 118D dp

这道题的思路可能来源于步兵后面必须跟骑兵,反之亦然,那么一个兵种当前的状态肯定是由另一个兵种上一个的状态推来的(即取用该当前取用的兵种之前)。接下来就要考虑怎么控制每次取用多少个人了,由题意可知,每次取用不得超过k1或k2,
我们从1 - n1和从1 - n2表示骑兵和步兵当前的数量表示当前状态,达到当前状态有两种情况,一种情况是上一个状态结尾时骑兵,我们需要去步兵来达到当前状态,另一种情况是上一个状态结尾是步兵,我们需要取骑兵来达到当前状态,这样就形成了基本的递推。我们需要初始化不取用骑兵,取用1 - k1个步兵 和 不取用步兵,取用1 - k2个骑兵,方案数均为1(均可一步到达)。
由于这道题我也没有想明白为什么能想到这样实现,所以没有办法讲明白具体的思路推导,只能说一说自己理解的(bushi
这道题大概有两种实现思路,但思路完全是一样的,我就全放在下面了(个人更喜欢3维的做法,感觉更清晰)

三维

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e8;

void slove()
{
    ll n1, n2, k1, k2;
    cin >> n1 >> n2 >> k1 >> k2;
    ll dp[110][110][3];
    //dp[ 使用的步兵数量 ] [ 使用的骑兵数量 ] [ 当前状态结尾是步兵(1)还是骑兵(2) ]
    for(ll i = 1; i <= k1; i ++)
    {
        dp[i][0][1] = 1;
    }
    for(ll i = 1; i <= k2; i ++)
    {
        dp[0][i][2] = 1;
    }
    for(ll i = 1; i <= n1; i ++)
    {
        for(ll l = 1; l <= n2; l ++)
        {
            ll num1 = min(i, k1);
            ll num2 = min(l, k2);
            for(ll op1 = 1; op1 <= num1; op1 ++)
            {
                dp[i][l][1] += dp[i - op1][l][2];
                dp[i][l][1] %= mod;
            }
            for(ll op2 = 1; op2 <= num2; op2 ++)
            {
                dp[i][l][2] += dp[i][l - op2][1];
                dp[i][l][2] %= mod;
            }
        }
    }
    cout << (dp[n1][n2][1] + dp[n1][n2][2]) % mod << endl;
    return ;
}
int main()
{
    ll t = 1;
    // cin >> t;
    while(t --){
        slove();
    }
    return 0;
}

四维

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e8;

ll dp[220][3][110][110];
//dp[ 使用的骑兵和步兵数量之和 ] [ 当前状态结尾是步兵(0)还是骑兵(1) ] [ 剩余的步兵数量 ] [ 剩余的骑兵数量 ]
void slove()
{
    ll n1, n2, k1, k2;
    cin >> n1 >> n2 >> k1 >> k2;
    ll r1 = min(k1, n1), r2 = min(k2, n2);
    for(ll i = 1; i <= r1; i ++)
    {
        dp[i][0][n1 - i][n2] = 1;
    }
    for(ll i = 1; i <= r2; i ++)
    {
        dp[i][1][n1][n2 - i] = 1;
    }
    for(ll i = 2; i <= n1 + n2; i ++)
    {
        for(ll op1 = 0; op1 <= i && op1 <= n1; op1 ++)
        {
            for(ll op2 = 0; op2 <= i && op2 <= n2; op2 ++)
			{
                if(op1 + op2 != i)																																																																																																													
                    continue ;
                ll num1 = min(op1, k1);
                ll num2 = min(op2, k2);
            	for(ll j = 1; j <= num1; j ++)
                {
                    dp[i][0][n1 - op1][n2 - op2] += dp[i - j][1][n1 - op1 + j][n2 - op2];
                    dp[i][0][n1 - op1][n2 - op2] %= mod;
                }
                
            	for(ll j = 1; j <= num2; j ++)
                {
                    dp[i][1][n1 - op1][n2 - op2] += dp[i - j][0][n1 - op1][n2 - op2 + j];
                    dp[i][1][n1 - op1][n2 - op2] %= mod;
                }
            }    
        }
    }
//    cout << dp[n1 + n2][1][0][0] << " " << dp[n1 + n2][0][0][0] << endl;
    cout << (dp[n1 + n2][1][0][0] + dp[n1 + n2][0][0][0]) % mod << endl;
}
int main()
{
    ll t = 1;
    // cin >> t;
    while(t --)
    {
        slove();
    }
    return 0;
}

这题是变形的背包题,个人感觉这题可能直接暴力搜索可能会简单很多,但毕竟再练dp就一直在想dp思路,假了一版思路之后就去研究题解的做法了,看题解之前没有啥思路,但看了之后感觉理所当然的就应该那样做。。。还是要多练啊,现在看dp没之前那么困难了

标签:取用,ll,CodeForces,k2,k1,步兵,118D,dp
From: https://www.cnblogs.com/-zzuxx-/p/18445406

相关文章

  • Codeforces Round 975 (Div. 2)
    一万四参赛,VP赛时60A.MaxPlusSize一定选择最大值,若有多个最大值,优先选在奇数位置的最大值,后间隔涂上红色。#include<bits/stdc++.h>usingnamespacestd;constintN=105;intT,n,a[N];intmain(){ scanf("%d",&T); while(T--){ scanf("%d",&n); intm......
  • Codeforces Global Round 19 E. Best Pair
    \(cnt\)的取值种类数不超过\(\sqrtn\)。因此我们可以枚举\(cnt\)然后贪心选最大的值。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64usingvi=vector<int>;usingpii=pair<int,int>;voidsolve()......
  • CF2020(Codeforces Round 976 (Div. 2))
    CF2020A.FindMinimumOperations难度:红转换进制,每一位上数字相加。#include<bits/stdc++.h>#definelllonglongusingnamespacestd;llt,n,k,ans;intmain(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>t;while(t--){cin&......
  • 单调队列优化 DP
    单调队列可以\(O(n)\)求出子区间的最值,且顺序上要求区间的左端点和右端点单调不降。引入P1886滑动窗口/【模板】单调队列定义一个队列\(q\),我们让\(q\)中的元素满足单调不降。因此当\(x\)入队时,从前往后让不在当前范围的元素出队,从后往前将\(<x\)的元素全部出队,然......
  • Codeforces Round 956 (Div. 2)
    无法评价,不知道是我傻逼还是题傻逼。A.ArrayDivisibility题意让你构造一个长度为\(n\)的序列,满足对于每一个\(i\)\((i\in[1,n])\),让\(a_j\)之和为\(i\)的倍数,\(j\)能被\(i\)整除。换句话说,让你构造一个长度为\(n\)的序列,满足\(\sum_{j|i}a_j\)能被\(i\)......
  • Codeforces2014E Rendez-vous de Marian et Robin(分层图最短路)
    题意给定一个无向图,含有\(n\)个点和\(m\)条边。题解点击查看代码#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;constexpri64inf=1e18;voidsolve(){intn,m,h;cin>>n>>m>>h;vector<vector<......
  • Codeforces Round 974 (Div. 3) - D题
    这道题题意就是你有k个工作,每个工作都有一个时间区间左边界l和右边界r,妈妈和哥哥要来看你,时长为d,题目要求求出1.哥哥看你的这段时间工作时间段重叠最多是多少?2.妈妈看你的这段时间工作时间段重叠最少是多少?这道题如果硬做的话可能就是线段树了(蒟蒻暂时没有想到其他的做法),但如果......
  • Codeforces Round 973 (Div. 2) - D题二分
    首先这道题的一个坑点就是求max(a[1],a[2],...,a[n])和求min(a[1],a[2],...,a[n])是完全独立的,不会相互影响(可能是我读题能力太差,一直卡在这点了。。。)这道题二分是一种很好想的方法,题中提到max和min,我们就可以想到只要让最大值最小,让最小值最大就可以达到我的目的了,二分的......
  • Codeforces Round 976 (Div. 2)
    VP的这一场,真的唐完了。。。A.FindMinimumOperations题意给你一个\(n\)和\(k\),每次操作可以让\(n\)减去一个\(k\)的\(x\)次方,即\(n-k^x\)。问你最少操作几次能够让\(n\)变成\(0\)。思路我们先考虑如果\(k\)是\(2\)的情况,那么题目就转化成了\(n\)的......
  • 每日OJ题_牛客_DP2跳台阶_动态规划_C++_Java
    目录牛客_DP2跳台阶_动态规划题目解析C++代码Java代码牛客_DP2跳台阶_动态规划跳台阶_牛客题霸_牛客网题目解析        当前值只和数组的前两个值有关,在往前面的就无关了,所以没必要申请一个数组,直接使用两个变量即可,这样空间复杂度就满足要求了。C++代码......