• 2019 CSP-S Day2-T1 Emiya 家今天的饭(DP)


    题目

    Description

    在这里插入图片描述
    在这里插入图片描述

    Input

    在这里插入图片描述

    Output

    输出到文件 meal.out 中。
    仅一行一个整数,表示所求方案数对 998, 244, 353 取模的结果。

    Sample Input

    Sample Input1
    2 3
    1 0 1
    0 1 1
    在这里插入图片描述
    Sample Input2
    3 3
    1 2 3
    4 5 0
    6 0 0
    在这里插入图片描述
    Sample Input3
    5 5
    1 0 0 1 1
    0 1 0 1 0
    1 1 1 1 0
    1 0 1 0 1
    0 1 1 0 1

    Sample Output

    Sample Output1
    3

    Sample Output2
    190

    Sample Output3
    742

    Data Constraint

    在这里插入图片描述

    题解

    • 作为联赛的Day2-T1,理论上应该AC是应该不难的,
    • 但是,这道题偏偏难倒了我。。。考场上简直觉得这是神仙题!!!
    • 赛后一想,这题并没有那么难,随着一档又一档部分分的实现,我离正解越来越近,
    • 不得不说,这是一道质量很高的题(其实联赛题质量都挺高的),主要体现在分了许多子任务和它们各自的不同与联系。
    • 开始进入正题——
    • 观察满分数据大小,猜测正解的复杂度应该是 O ( n 2 m ) O(n^2m) O(n2m)的。
    • 先想最简单的暴力怎么做?
    • 暴力搜索每种烹饪方法选用什么食材或不选该种烹饪方法,时间复杂度 O ( m n ) O(m^n) O(mn),期望得分32分。
    • 发现每次DFS到下一种烹饪方法时,最多一种食材数量改变,
    • 又看到有64分数据的 m m m特别小,想到可以DP,先枚举总共选多少种烹饪方法,再设 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]表示当前选到第 i i i种食材, m m m种食材分别选了 j , k , l j,k,l j,k,l个的方案数,如果 m = 1 m=1 m=1 2 2 2多余的 k , l k,l k,l就恒为 0 0 0
    • 每次从 f [ i − 1 ] [ j ] [ k ] [ l ] f[i-1][j][k][l] f[i1][j][k][l] f [ i − 1 ] [ j − 1 ] [ k ] [ l ] f[i-1][j-1][k][l] f[i1][j1][k][l] f [ i − 1 ] [ j ] [ k − 1 ] [ l ] f[i-1][j][k-1][l] f[i1][j][k1][l] f [ i − 1 ] [ j ] [ k ] [ l − 1 ] f[i-1][j][k][l-1] f[i1][j][k][l1]转移,须保证 j , k , l j,k,l j,k,l不大于 n 2 frac{n}{2} 2n
    • 时间复杂度 O ( n 5 ) O(n^5) O(n5),其中 n n n的指数会随 m m m增大而增大,所以 m > 3 m>3 m>3时是过不去的,期望得分64分。
    • 如果 m > 3 m>3 m>3的话,该怎么记录呢?开若干维状态?还是状态压缩?还是什么奇技淫巧?似乎都是不行的。。。
    • 要是顺着这种思路下去,这题就只能拿到这么多分了!
    • (这就是考场上的我,心凉~~~)
    • 重新回到题目,要求每种食材使用次数不能超过总数的一半,想到容斥,用总方案数减去不合法的。
    • 总方案数用递推很好实现,那么不合法的呢?
    • 会发现无论怎么选,也最多只会有一种食材出现不合法(这个重要结论似乎很显然,可没想到是就是没想到),,,
    • 因此,我们枚举哪种食材不合法,然后简单地DP,
    • 记录当前总共选了多少个,且有多少个是不合法的那种。
    • 时间复杂度 O ( n 3 m ) O(n^3m) O(n3m),期望得分84.
    • 显然要优化掉一个 n n n,考虑减少一维状态,
    • 改成记录不合法减去合法的个数,最后这一维大于 0 0 0的部分就是可以累计的答案,
    • 时间复杂度 O ( n 2 m ) O(n^2m) O(n2m),期望得分100!

    代码

    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define ll long long
    #define md 998244353
    ll a[110][2010],f[110][210],sum[2010];
    int main()
    {
       freopen("meal.in","r",stdin);
       freopen("meal.out","w",stdout);
       int n,m,i,j,k,l;
       scanf("%d%d",&n,&m);
       for(i=1;i<=n;i++)
       	for(j=1;j<=m;j++) scanf("%d",&a[i][j]);
       f[0][0]=1;
       for(i=1;i<=n;i++)
       {
       	sum[i]=0;
       	for(j=1;j<=m;j++) sum[i]=(sum[i]+a[i][j])%md;
       	for(j=0;j<=i;j++) 
       	{
       		f[i][j]=f[i-1][j];
       		if(j) f[i][j]=(f[i][j]+f[i-1][j-1]*sum[i])%md;
       	}
       }
       ll ans=0;
       for(i=1;i<=n;i++) ans=(ans+f[n][i])%md;
       for(k=1;k<=m;k++)
       {
       	f[0][n]=1;
       	for(i=1;i<=n;i++)
       	{
       		for(j=-i;j<=i;j++)
       		{
       			f[i][j+n]=f[i-1][j+n];
       			if(j>-i) f[i][j+n]=(f[i][j+n]+f[i-1][j-1+n]*a[i][k])%md;
       			if(j<i) f[i][j+n]=(f[i][j+n]+f[i-1][j+1+n]*(sum[i]-a[i][k]+md))%md;
       		}
       	}
       	for(i=1;i<=n;i++) ans=(ans-f[n][i+n]+md)%md;
       }
       printf("%lld",ans);
       fclose(stdin);
       fclose(stdout);
       return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    linux分析工具之vmstat详解
    linux分析工具之top命令详解
    hadoop之yarn详解(命令篇)
    hadoop之yarn详解(基础架构篇)
    linux分析利刃之sar命令详解
    hadoop之mapreduce详解(优化篇)
    linux之find命令详解
    一个毫无用处的公众号封面生成器
    一个简单的计时器对比各种可迭代对象定义方式的速度区别
    python中的迭代器和生成器
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910054.html
Copyright © 2020-2023  润新知