*题解:P10242 [THUSC 2021] Emiya 家明天的饭

题目链接

解析

由于人数 \(n\) 很小,考虑对人数进行状压。拆贡献,设 \(f_{i,S}\) 表示满足集合 \(S\) 内所有朋友的要求时,第 \(i\) 位朋友对结果做的贡献。那么所有朋友做的贡献加起来的结果 \(\sum_{i\in S} f_{i,S}\) 即为朋友集合为 \(S\) 时的结果,所有集合结果取最大值即为答案。

考虑怎么求 \(f\)。设 \(T_j\) 表示喜欢第 \(j\) 道菜的朋友集合,那么第 \(j\) 道菜满足集合 \(S\) 内所有朋友的要求当且仅当 \(S\subseteq T_j\)。于是,对于 \(f_{i,S}\),我们有:

\[f_{i,S} = \sum_{S \subseteq T_j} a_{i,j} \]

相当于对每个 \(i\) 都做一遍 SOSDP。时间复杂度 \(O(n^2 2^n)\)

代码

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
//#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<int,int> pii;
const int N = (1 << 20) + 5,M = 20 + 5,P = 2000000,mod = 1e9 + 7,mod2 = 1e9 + 7,b1 = 131,b2 = 13331;
ll f[M][N],a[M][N],t[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
			if(a[i][j] >= 0){
				t[j] |= (1 << (i - 1));
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(t[j] & (1 << (i - 1))){
				f[i][t[j]] += a[i][j];
			}	
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1 << n);j++){
			if(!(j & (1 << (i - 1)))){
				for(int k=1;k<=n;k++)if(j & (1 << (k - 1))){
					f[k][j] += f[k][j ^ (1 << (i - 1))];
				}
			}
		}
	}
	ll res = 0;
	for(int i=0;i<(1 << n);i++){
		ll x = 0;
		for(int j=1;j<=n;j++){
			x += f[j][i];
		}
		res = max(x,res);
	}
	cout<<res;
	return 0;
}
posted @ 2026-06-16 20:10  yutar  阅读(6)  评论(0)    收藏  举报