bitset优化dp Music Problem

来源

题意:就是给定 n < = 1 e 5 n<=1e5 个数,从中选取k个数,要求和是3600的倍数, T < = 60 T<=60组数据

①思路:dp[i]表示i这一位能被得到,然后更新就是从上一个能到达的状态更新过来,但复杂度无疑炸了。
可以用bitset来优化,每次更新不用3600,而是直接移位(<<),然后在取个模(>>),最后或上去就 完成一次更新
复杂度可以降(32/64)倍。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
typedef long long ll;
const ll mod = 1e9+7;

int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		bitset<man>b,b1,b2;
		//b[i]表示可以组成i这个数(%3600之后的)
		b[0] = 1;//起始0是存在的
		for(int i = 1;i <= n;i++){
			int x;
			cin >> x;
			x %= 3600;
			b1 = b2 = b << x;
			// b << x 的意思就相当于往左移x位
			// emm 加入i位是1,就是可以构成i,那么左移了i+x就存在了
			// 把所有的(0~3600)数都往左移x位,即对之前的状态都更新了
			b2 = b2 >> 3600;
			//然后b2 >>3600 其实就相当于减去3600(取余),对于新状态 3600~7200内对3600取余

			//上一个状态的b(不选x) 跟 更新之后的状态(选x) 或一下  就表示2个都有  
			b |= b1;
			b |= b2;
		}
		cout << (b[3600] ? "YES\n" : "NO\n");
	}
	return 0;
}

②考虑大于3600个数一定存在3600倍数,因为先对所有数取模,然后求一个前缀和,只要前缀和中有2个相同,就代表有3600倍数,(因为区间和是0,3600倍数) ,大于3600个数中肯定有存在2个相同的数。剩下小于3600的直接暴力。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 1e5+10;
#define IOS ios::sync_with_stdio(0)
template <typename T>
inline T read(){T sum=0,fl=1;int ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
for(;isdigit(ch);ch=getchar())sum=sum*10+ch-'0';
return sum*fl;}
template <typename T>
inline void write(T x) {static int sta[35];int top=0;
do{sta[top++]= x % 10, x /= 10;}while(x);
while (top) putchar(sta[--top] + 48);}
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}
else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;
int a[man];
int dp[3605];
int tp[man];

int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		for(int i = 1;i <= n;i++){
			cin >> a[i];
			a[i] %= 3600;
		}
		if(n>3600){
			cout << "YES\n";
		}else{
			memset(dp,-1,sizeof(dp));
			dp[0] = 0;
			int id;
			for(int i = 1;i <= n;i++){
				id = 0;
				for(int j = 0;j < 3600;j++){
					if(dp[j]==-1)continue;
					int v = (j + a[i])%3600;
					tp[id++] = v;//不能直接更新,不然现在更新了,然后又去更新其他的,重复利用了
				}
				for(int j = 0;j < id;j++)dp[tp[j]] = 1;
				//for(int j = 0;j < 3600;j++)cout << dp[j] << " ";
				//cout <<endl;
			}
			cout << (dp[0] ? "YES\n" : "NO\n");
		}
	}
	return 0;
}
原创文章 93 获赞 12 访问量 8987

猜你喜欢

转载自blog.csdn.net/weixin_43571920/article/details/105530582