1. 程式人生 > 其它 >ACG054 B - Greedy Division

ACG054 B - Greedy Division

目錄

題目

https://atcoder.jp/contests/agc054/tasks/agc054_b

思路

怎麼看,這題應該要DP,但是描述一個狀態是很大問題:你怎麼知道選了哪些橙子(共有\(2^{100}\)種情況)?

經過一番漫長的思考後,師父,我悟了!

其實,並不用描述選了哪些橙子,題目的關鍵就在排列\(P\)和那兩人拿橘子的順序是一一對應的,比如說,Takahashi 依次取了1,5,2號橙子,Aoki依次取了4,3號橙子,那麼,有且僅有一個\(P\)和這種情況對應,且該\(P\)只對應這種情況(用心體會下)

這樣,問題就簡單多了,設\(f_i\)表示拿了\(i\)個橙子,且它們的重量和恰好等於所有橙子重量和的一半,的方案數.答案就應該是(別忘了取模):

\[ans=\sum^n_{i=1}f_i\cdot i!\cdot (n-i)! \]

\(i!\)是其中一個人拿橙子的順序的方案數,\((n-i)!\)就是另一個人拿橙子的順序的方案數

如何得到\(f\)?

DP:\(f_{i,j}\)表示拿了\(i\)個橙子,總重量為\(j\)的方案數,過程有點像揹包問題,應該都會了吧

程式碼

#include <iostream>
#include <cstdio>
using namespace std;
#define N 110
#define ll long long
#define mod 998244353ll
ll f[N][10010];
ll n , w[N] , sumw;
ll fac[N];

int main() {
	fac[0] = 1;
	for(int i = 1 ; i <= 100 ; i++)
		fac[i] = fac[i - 1] * i % mod;
	
	cin >> n;
	for(int i = 1 ; i <= n ; i++) {
		cin >> w[i];
		sumw += w[i];
	}
	if(sumw % 2 == 1) {
		cout << 0;
		return 0;
	}
	
	f[0][0] = 1;
	for(int i = 1 ; i <= n ; i++)//列舉第i個橙子
		for(int j = n ; j >= 0 ; j--)//一共j個橙子(除了i),注意倒敘列舉
			for(int k = sumw ; k >= 0 ; k--)//重量(除了i)
				f[j + 1][k + w[i]] += f[j][k],
				f[j + 1][k + w[i]] %= mod;
	
	ll ans = 0;
	for(int i = 1 ; i <= n ; i++)//求答案
		ans = (ans + fac[i] * fac[n - i] % mod * f[i][sumw / 2] % mod) % mod;
	
	cout << ans << endl;
	return 0;
}