题目名称看样子灵感来自于日本动画片“卫宫家今天的饭”。
这道题的难度是“提高+/省选-”,算是提高组里比较难的。数据范围分的很细,解题方法跟数据范围关系比较大。对于新手来说,可以从数据范围入手,一步一步推导出最终的正解。
前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