Music Problem 背包dp +(抽屉原理 || 二进制优化)

阅读 40

2022-01-31

Music Problem
思路:
常规思路:
模数取 3600 3600 3600,很容易想到 T ∗ n ∗ 3600 T*n*3600 Tn3600 的做法,空间上优化第一维,但时间复杂度太大
数学优化:
题解有人讲到抽屉原理:一个由 n n n 个数组成的数列 一定能找出若干个连续的数使它们之和能被 n n n 整除。特判掉 n > = 3600 n>=3600 n>=3600 的情况,常规思路就可以通过了,我直呼 N B NB NB,不愧是清华的大佬
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int f[2][4000];

void work()
{
	cin >> n;
	if(n >= 3600){
        for(int i = 1, x; i <= n; ++i) cin >> x;// 这里要读完再输出,不然会wa
        cout << "YES" << endl;return;
    }
	for(int i = 0; i <= mod; ++i)
		for(int j = 0; j < 2; ++j)
			f[j][i] = 0;
	int op = 1;
	for(int i = 1, x; i <= n; ++i, op ^= 1){
		cin >> x;
		x %= mod;
		f[op][x] = 1;
		for(int j = mod - 1; j >= 0; --j){
			f[op][j] = max(f[op][j], f[op^1][j]);
			int k = (x + j) % mod;
			f[op][k] = max(f[op][k], f[op^1][j]);
		}
	}
	cout << (f[op^1][0] ? "YES" : "NO") << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

二进制优化思路:
a i   %   m o d a_i \ \% \ mod ai % mod 分成 m o d mod mod 组,然后一组有 c i c_i ci 个数,然后类似多组背包二进制优化的方法,可以很快的通过,很好用的优化思路,可以直接莽这个方法
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int f[2][4000];
int cnt[4000];
void work()
{
	cin >> n;
	for(int i = 0; i < mod; ++i)
		for(int j = 0; j < 2; ++j)
			f[j][i] = 0;
	for(int i = 0; i < mod; ++i) cnt[i] = 0;
	for(int i = 1, x; i <= n; ++i) cin >> x, cnt[x%mod]++;
	if(cnt[0]){// 速度大幅提升主要是因为这个
		cout << "YES\n";return;
	}
	int op = 1;
	for(int i = 1; i < mod; ++i){
		if(!cnt[i]) continue;
		vector <int> v;
		int c = cnt[i];
		for(int j = 1; j <= c; j <<= 1)
		{
			v.push_back(i * j % mod);
			c -= j;
		}
		if(c) v.push_back(i * c % mod);
		for(auto d : v)
		{
			f[op][d] = 1;
			for(int j = mod - 1; j >= 0; --j)
			{
				f[op][j] = max(f[op][j], f[op^1][j]);
				int k = (d + j) % mod;
				f[op][k] = max(f[op][k], f[op^1][j]);
			}
			op ^= 1;
		}
	}
	cout << (f[op^1][0] ? "YES" : "NO") << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

betset优化:
b i t s e t bitset bitset 实现这道题的神奇做法
代码简洁且高效,就是稍微有点抽象
S T L STL STL N B NB NB
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 4e3 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;

void work()
{
	cin >> n;
	bitset <maxn * 2> f;
	for(int i = 1, x; i <= n; ++i){
		cin >> x; x %= mod;
		f |= f << x | f >> (mod - x);
		f[x] = 1;
	}
	cout << (f[0] ? "YES" : "NO") << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

精彩评论(0)

0 0 举报