CSP-S2019学习笔记:Emiya家今天的饭

题目名称看样子灵感来自于日本动画片“卫宫家今天的饭”。

这道题的难度是“提高+/省选-”,算是提高组里比较难的。数据范围分的很细,解题方法跟数据范围关系比较大。对于新手来说,可以从数据范围入手,一步一步推导出最终的正解。

前8组数据,n不超过10,m等于2或3。可以直接用深度优先搜索暴力枚举所有的组合情况。

第9~16组数据,n为40,m仍然等于2或3。因为n比较大,若仍然用暴力枚举,则会超时。考虑到m等于2或m=3,可考虑用背包动态规划的思路来做,比如有三道菜A、B、C,那么有四种选法:不选、选A、选B、选C。

第17~21这四组数据,n = 40,m = 500,这种情况下,可将当前列的菜看成一种菜,剩余其他列的菜看成另一道菜。仍然是一个背包问题。这种解法有四层循环,其复杂度为m * n^3。将m和n的最大值代进去,则最多需要循环500 * 40^3 = 3200万次,这个计算量电脑还是可以承受的。

最后四组数组,n = 100, m = 2000,如果按上一步的解法,需要循环计算2000 * 100^3 = 20亿次,会超时。此时可将选某列菜与不选某列菜的差做为动态规划中数据的下标,这样数组可节省掉一维,循环次数变为m * n^2 = 2000 * 100^2 = 2千万次。

总体而言,这是一道优秀的动态规划题。学完这道题,对动态规划的理解会更深入一层。

附AC代码:

//Zheng Haishu, 2020-2-15
# include <cstdio>
# include <iostream>
# include <memory.h>
using namespace std;

const int N = 110;
const int M = 2010;
const int offset = 110;
const int mod = 998244353;

typedef long long LL;

int n, m, a[N][M];
LL s[N], dp[offset][2*offset], ans;


int main()
{
    //freopen("meal4.in", "r", stdin);

    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
        }
    }

	ans = 1;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
		    s[i]=(s[i] + a[i][j]) % mod;
		}
		ans = ans * (s[i] + 1) % mod;
	}

	for(int col = 1; col <= m; col++)
	{
		memset(dp, 0, sizeof(dp));
		dp[0][offset] = 1; //使用offset是为了偏移数组下标,使下标不小于0
		for(int i = 1; i <= n; i++)//i表示行
        {
            for(int j = -1 * i; j <= i; j++)//j表示不选第col列减去选第col列
            {
                //选第col列
                dp[i][j+offset] += dp[i-1][j-1+offset] * a[i][col];
                dp[i][j+offset] %= mod;

                //不选第col列
				dp[i][j+offset] += dp[i-1][j+1+offset] * ((s[i] - a[i][col] + mod) % mod);
				dp[i][j+offset] %= mod;

				dp[i][j+offset] += dp[i-1][j+offset];
				dp[i][j+offset] %= mod;
			}
        }

		for(int i = 1; i <= n; i++)
		{
		    ans = (ans - dp[n][i+offset] + mod) % mod;
		}
	}

	printf("%lld\n", ans - 1); //减掉都不减即dp[0][offset]

	return 0;
}

了解信息学奥赛请加微信307591841或QQ群581357582

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值