1.首先通过问题取反,将找都小于k/2的情况,转化为找大于k/2的情况,而这种最多有一个大于所有的一半,而每一种菜不一样,考虑枚举每一种菜
2.因为可以不取,所以多的与剩下的各有自由度,所以考虑三维dp,dp[i][j][k],每一轮dp在选定的最长列为mj,所以dp[i][j][k]=dp[i-1][j-1][k]*(a[i][mj])+dp[i-1][j][k-1]*(sum[i]-a[i][mj])+dp[i-1][j][k],j为最长的个数,k为其余的个数(不区别),每次只更新有效的(dp只要考虑更新完一维所有有效的即可进行下一轮),初始只有dp【0】【0】【0】是有效的,为1种情况(理解),最后有效的就是dp【i】【j】【k】(j>k)
3.复杂度为o(n^3*m)考虑优化,每次更新是j,k轮流加,所以考虑压缩二三维,dp[i][j]这里的j=j-k,由于可能出现负数所以,二维所有+n。有效值为dp[i][j],j>n;
4.放缩考虑逐步优化
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=998244353; inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } return x*f; } inline void write(int x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int a[105][2005],i,j,k; int sum[105]; int dp[105][4005]; ll sum1,sum2; int main () { int n,m; n=read(); m=read(); for(i=1;i<=n;++i) { for(j=1;j<=m;++j) { a[i][j]=read(); sum[i]=(sum[i]+a[i][j])%mod; } } for(j=1;j<=m;++j) { memset(dp,0,sizeof(dp)); dp[0][n]=1; for(i=1;i<=n;++i) { for(k=n-i;k<=n+i;++k) { dp[i][k]=(1ll*dp[i-1][k]+(1ll*dp[i-1][k+1]*(sum[i]-a[i][j]))%mod+(1ll*dp[i-1][k-1]*a[i][j])%mod)%mod; } } for(i=1;i<=n;++i) { sum1=(sum1+dp[n][i+n])%mod; } } sum2=(sum[1]+1)%mod; for(i=2;i<=n;++i) { sum2=(sum2*(sum[i]+1))%mod; } write((((sum2-sum1-1)%mod)+mod)%mod); return 0; }
dp优化(
最新推荐文章于 2023-02-22 09:52:20 发布