题目:链接:https://ac.nowcoder.com/acm/contest/24213/1014 来源:牛客网

求方法个数,时间给了1s于是不得不放弃暴力的想法(qaq)那就dp! 先想总的方法数:显然是4的n次方,那么我们就在找一下不会亏本的方法数,求一下gcd,输出答案就好了。

状态表示:我的状态表示比较麻烦(写完之后看了别的佬的代码)用了三维。

dp[i][j][k],i表示买了i张,j为当前这张的中奖金额,k为盈利了多少,dp[i][j][k]为方法数。

状态计算:显然每张彩票都要用上,所以只能由前一张彩票转移而来,那前一张的盈利数额应该为k-(j-3)即当前盈利数额减去这张的盈利额,于是状态转移方程就有了:

dp[i][j][k]=dp[i-1][1][k-(j-3)]+dp[i-1][2][k-(j-3)]+dp[i-1][3][k-(j-3)]+dp[i-1][4][k-(j-3)]

由于可能亏本,所以k的值可能为负值,所以我们给k统一加上一个定值让其不为负。上代码:

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&-x)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
ll dp[31][5][101]={0};
void solve(){
    int n;
    cin>>n;
    if(n==0){
        cout<<1<<'/'<<1;
        return;
    }//n==0需要特判。
    int w=2*n;//加上w让盈利值不为负
    dp[1][1][w-2]=1;
    dp[1][2][w-1]=1;
    dp[1][3][w]=1;
    dp[1][4][w+1]=1;//初始化
    for(int i=2;i<=n;i++){
        for(int j=1;j<=4;j++){
            for(int k=-(2*i);k<=i;k++){
                dp[i][j][k+w]=dp[i-1][1][k+w-(j-3)]+dp[i-1][2][k+w-(j-3)]+dp[i-1][3][k+w-(j-3)]+dp[i-1][4][k+w-(j-3)];//转移方程
            }
        }
    }
    int h=n;//h为最多盈利数
    ll ans=0;
    for(int i=1;i<=4;i++){
        for(int j=w;j<=w+h;j++) ans+=dp[n][i][j];//把1到4元的方法数加起来即为答案。
    }
    ll sum=1;
    for(int i=1;i<=n;i++) sum*=4;//sum为总方法数
    ll c=gcd(ans,sum);
    ans/=c;
    sum/=c;
    cout<<ans<<"/"<<sum;
}
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    solve();
    return 0;
}




js