题意:就是给定 个数,从中选取k个数,要求和是3600的倍数, 。
①思路: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;
}