哇,这也是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的,不过我懒得写了(池沼)