哇,这也是DP?

修补骑士一开始当背包来想:我们要写4个数组来DP吗?每一个都记录可行的最大次数吗?但他很快发现——这个主要是可以积累,你不知道他上一个可行究竟剩下来多少!

看了看题解:我们要维护“成功数量”与“剩余数量”,只写一维不好维护,我们就拿不到每种成功情况下对应的剩余,就没法持续DP运算。那我们就写二维,维护两个方面:“买x张彩票”“得到了y元”——值为方案数,是否成功我们最后来维护

实际上也这就是DP的一种思路:用之前的情况推出现在的情况来进行,每一种情况我们记录可能的方法,用上面的方法推下面的,又加上n最大为30,支持我们暴力写个背包!

AC代码:

#define int long long
using namespace std;

signed main() 
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    int n;
    
    cin>>n;
    
    vector<vector<int>> dp(n + 3,vector<int>(4 * n + 3,0));
       
    //最好就是:特殊情况特殊处理,不要太过信任自己代码有什么健壮性
    
    if(n == 0)
    {
        cout<<"1/1"<<endl;
        
        return 0;
    }
    
    if(n == 1)
    {
        cout<<"1/2"<<endl;
        
        return 0;
    }
    
    dp[1][1] = 1;
    dp[1][2] = 1;
    dp[1][3] = 1;
    dp[1][4] = 1;
    
    for(int r = 1;r < n;r++)
    {
        for(int y = r;y <= 4 * n - 4;y++)
        {
            dp[r + 1][y + 1] += dp[r][y];
            dp[r + 1][y + 2] += dp[r][y];
            dp[r + 1][y + 3] += dp[r][y];
            dp[r + 1][y + 4] += dp[r][y];            
        }
    }
    
    //对于n而言,不亏本的从3 * n开始走到4 * n
    
    int ans = 0;
    
    for(int y = 3 * n;y <= 4 * n;y++)
    {
        ans += dp[n][y];
    }
    
    int all = pow(4,n);
    
    int thegcd = gcd(all,ans);
    
    all /= thegcd;
    
    ans /= thegcd;
    
    cout<<ans<<"/"<<all<<endl;
    
    //幸好n不是很大,要不然复杂度就坏了
    
    return 0;
}

顺带一提,这里很明显只能增加钱财不能减少,我们DP只是维护x张票y个钱有n个方式,但是x我们是单调增加的,所以说理论上我们也是可以优化为一维DP的,不过我懒得写了(池沼)