zoukankan      html  css  js  c++  java
  • NOIP2019 Emiya 家今天的饭 [提高组]

    题目:Emiya 家今天的饭

    网址:https://www.luogu.com.cn/problem/P5664


    这道题质量很高。

    算法1 暴力 32pts

    首先,观察到对于每一种烹饪方法,我们有两种选择:要么不选任何一道菜(食材),要么任选。
    最后判断是否合法即可。

    代码:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int SIZE = 100 + 5, mod = 998244353;
    int n, m, ans = 0, a[SIZE][SIZE], cnt[SIZE];
    void dfs(int cur, long long tmp)
    {
    	if(cur == n)	
    	{
    		int sum = 0, val = 0;
    		for(int i = 0; i < m; ++ i) sum += cnt[i];
    		if(!sum) return;
    		for(int i = 0; i < m; ++ i) val = max(val, cnt[i]);
    		if(val <= sum / 2) ans = (ans + tmp) % mod;
    		return;
    	}
    	dfs(cur + 1, tmp);
    	for(int i = 0; i < m; ++ i)
    	{
    		if(a[cur][i] == 0) continue;
    		++ cnt[i];
    		dfs(cur + 1, tmp * a[cur][i] % mod);
    		-- cnt[i];
    	}
    	return;
    }
    int main()
    {
    	scanf("%d %d", &n, &m);
    	for(int i = 0; i < n; ++ i)
    	{
    		for(int j = 0; j < m; ++ j)
    		{
    			scanf("%d", &a[i][j]);
    		}
    	}
    	memset(cnt, 0, sizeof(cnt));
    	dfs(0, 1ll);
    	printf("%d
    ", ans);
    	return 0;
    }
    

    算法2 DP(背包) 64pts

    我们将整个问题看成背包。选择次数为容量,最后枚举合法状态。

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const long long mod = 998244353;
    const long long maxn = 100 + 5, maxm = 10;
    long long n, m, a[maxn][4], dp_1[maxn][maxn][maxn], dp_2[maxn][55][55][55];
    int main()
    {
    	scanf("%lld %lld", &n, &m);
    	for(long long i = 1; i <= n; ++ i)
    	{
    		for(long long j = 1; j <= m; ++ j)
    		{
    			scanf("%lld", &a[i][j]);
    		}
    	}
    	if(m == 2)
    	{
    		memset(dp_1, 0, sizeof(dp_1));
    		dp_1[0][0][0] = 1;
    		for(long long i = 1; i <= n; ++ i)
    		{
    			for(long long j = 0; j <= i; ++ j)
    			{
    				for(long long k = 0; k <= i; ++ k)
    				{
    					long long &ans = dp_1[i][j][k];
    					ans = dp_1[i - 1][j][k];
    					if(j) ans = (ans + dp_1[i - 1][j - 1][k] * a[i][1]) % mod;
    					if(k) ans = (ans + dp_1[i - 1][j][k - 1] * a[i][2]) % mod;
    				}
    			}
    		}
    		long long res = 0;
    		for(long long i = 1; i <= n >> 1; ++ i)
    		{
    			res = (res + dp_1[n][i][i]) % mod;
    		}
    		printf("%lld
    ", res);
    	}
    	else
    	{
    		memset(dp_2, 0, sizeof(dp_2));
    		dp_2[0][0][0][0] = 1;
    		for(long long i = 1; i <= n; ++ i)
    		{
    			for(long long j = 0; j <= i; ++ j)
    			{
    				for(long long k = 0; k <= i; ++ k)
    				{
    					for(long long l = 0; l <= i; ++ l)
    					{
    						long long &ans = dp_2[i][j][k][l];
    						ans = dp_2[i - 1][j][k][l];
    						if(j) ans = (ans + dp_2[i - 1][j - 1][k][l] * a[i][1]) % mod;
    						if(k) ans = (ans + dp_2[i - 1][j][k - 1][l] * a[i][2]) % mod;
    						if(l) ans = (ans + dp_2[i - 1][j][k][l - 1] * a[i][3]) % mod;
    					}
    				}
    			}
    		}
    		long long sum, res = 0;
    		for(long long i = 0; i <= n / 2; ++ i)
    		{
    			for(long long j = 0; j <= n / 2; ++ j)
    			{
    				for(long long k = 0; k <= n / 2; ++ k)
    				{
    					if(i + j + k > n || k > i + j) break;
    					if(i > j + k || j > i + k) continue;
    					res = (res + dp_2[n][i][j][k]) % mod;
    				}
    			}
    		}
    		if(!res) puts("0");
    		else printf("%lld
    ", res - 1);
    	}
    	return 0;
    }
    

    正解 DP + 容斥原理

    考虑(m>3)的其他情况。注意到选择次数超过(lfloor frac{k}{2} floor)的食材最多有一道。根据容斥原理,我们将所有情况计算出,减去不合法的状态即为所求。

    所有情况不难算出,不合法的状态是问题中最关键的一个。
    我们只需要枚举该食材即可。

    考虑:设(dp[i,j,k,l])代表考虑到第(i)道烹饪方案,食材(j)超过次数,选了(k)个,其余的选了(l)个。

    那么有:(dp[i,j,k,l]=dp[i-1.j,k,l]+dp[i-1,j,k-1,l]*a[i,j]+dp[i-1,j,k,l-1]*displaystylesum{a[i,s]})

    发现还可以优化:我们其实只关心超过次数与其余次数的差值。则必有:(dp[i,j,k]=dp[i-1,j,k]+dp[i-1,j,k-1]*a[i,j]+dp[i-1,j,k+1]*displaystylesum{a[i,s]});初始化:(dp[0,1~n,0]=1)(别忘了数组要挪位)。

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int mod = 998244353;
    const int maxn = 100 + 5, maxm = 2000 + 5;
    int n, m;
    long long ans = 0, a[maxn][maxm], dp[maxn][maxn], g[maxn][maxn << 1], sum[maxn];
    int main()
    {
    	scanf("%d %d", &n, &m);
    	memset(g, 0, sizeof(g));
    	memset(sum, 0, sizeof(sum));
    	for(int i = 1; i <= n; ++ i)
    	{
    		for(int j = 1; j <= m; ++ j)
    		{
    			scanf("%lld", &a[i][j]);
    			sum[i] = (sum[i] + a[i][j]) % mod;
    		}
    	}
    	memset(dp, 0, sizeof(dp));
    	for(int i = 0; i < n; ++ i) dp[i][0] = 1;
    	for(int i = 1; i <= n; ++ i)
    	{
    		for(int j = 1; j <= n; ++ j)
    		{
    			dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * sum[i]) % mod;
    		}
    	}
    	for(int i = 1; i <= n; ++ i) ans = (ans + dp[n][i]) % mod;
    	for(int k = 1; k <= m; ++ k)
    	{
    		memset(g, 0, sizeof(g));
    		g[0][n] = 1;
    		for(int i = 1; i <= n; ++ i)
    		{
    			for(int j = -n; j <= n; ++ j)
    			{
    				g[i][j + n] = (g[i - 1][j + n] + g[i - 1][j + n - 1] * a[i][k] + g[i - 1][j + n + 1] * (sum[i] - a[i][k])) % mod;
    			}
    		}
    		for(int i = n + 1; i <= n << 1; ++ i) ans = (ans + mod - g[n][i]) % mod;
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    软件对标分析
    alpha内测版发布
    第一阶段项目评审
    第一阶段意见汇总
    冲刺(二十)
    冲刺(十九)
    冲刺(十八)
    冲刺(十七)
    冲刺(十六)
    冲刺(十五)
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13698781.html
Copyright © 2011-2022 走看看