牛客算法周周练2 B Music Problem(DP,抽屉原理,二进制拆分)

链接:https://ac.nowcoder.com/acm/contest/5203/B
来源:牛客网

题目描述

Listening to the music is relax, but for obsessive(强迫症), it may be unbearable. HH is an obsessive, he only start to listen to music at 12:00:00, and he will never stop unless the song he is listening ends at integral points (both minute and second are 0 ), that is, he can stop listen at 13:00:00 or 14:00:00,but he can’t stop at 13:01:03 or 13:01:00, since 13:01:03 and 13:01:00 are not an integer hour time. Now give you the length of some songs, tell HH whether it’s possible to choose some songs so he can stop listen at an integral point, or tell him it’s impossible. Every song can be chosen at most once.
输入描述:
The first line contains an positive integer T(1≤T≤60), represents there are T test cases. For each test case: The first line contains an integer n(1≤n≤105), indicating there are n songs. The second line contains n integers a1,a2…an (1≤ai≤109 ), the ith integer ai indicates the ith song lasts ai seconds.
输出描述:
For each test case, output one line “YES” (without quotes) if HH is possible to stop listen at an integral point, and “NO” (without quotes) otherwise.

示例1

输入
复制

3
3
2000 1000 3000
3
2000 3000 1600
2
5400 1800

输出
复制

NO
YES
YES

说明

In the first example it’s impossible to stop at an integral point.In the second example if we choose the first and the third songs, they cost 3600 seconds in total, so HH can stop at 13:00:00In the third example if we choose the first and the second songs, they cost 7200 seconds in total, so HH can stop at 14:00:00

题目大意就是给你n个数,问是否可以从中选取若干个数使得他们的和为3600.
抽屉原理的应用:一个由n个数组成的数列 一定能找出若干个连续的数使它们之和能被n整除。
所以 n >= 3600 时输出 YES
证明:n个数记为a[1],a[2],…a[n].设置一个数组sum,其存储信息为sum[i] = a[1] + a[2] + …a[i];
情况一:存在一个k(1 <= k <= n),使得sum[k] % n == 0,那么就得证;
情况二:对于任意的k(1 <= k <= n),都有sum[k] % n != 0,那么余数的种类只有 1 到 n-1 总共 n-1 种情况,但是有 n 个 sum,由抽屉原理
可知必然有两个sum(假设为sum[i],sum[j],j > i)的余数相同。因此 (sum[j] - sum[i]) % n = (sum[j] % n - sum[i] % n) = 0;
所以 n >= 3600 时输出 YES
然后数据范围就压缩到了3600,用二进制拆分之后直接按照01背包来做就好。
具体思路解析
牛客算法周周练2 B Music Problem 题解

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<vector>
#define ls (p<<1)
#define rs (p<<1|1)
//#define mid (l+r)/2
#define over(i,s,t) for(register long long i=s;i<=t;++i)
#define lver(i,t,s) for(register long long i=t;i>=s;--i)
//#define int __int128
using namespace std;
typedef long long ll;//全用ll可能会MLE或者直接WA,试着改成int看会不会A
const ll N=1e5+7;
const ll INF=1e10+9;
const ll mod=3600;
const double EPS=1e-10;//-10次方约等于趋近为0
const double Pi=3.1415926535897;
 
ll n,m,dp[2][mod],a[N];
ll tot[mod],t;
int main()
{
    scanf("%lld",&t);
    while(t--)
    {
        memset(tot,0,sizeof tot);
        ll n;
        scanf("%lld",&n);
        over(i,1,n)
        {
            scanf("%lld",&a[i]);
            a[i]%=mod;
            tot[a[i]]++;
        }
        if(tot[0]){
            puts("YES");
            continue;
        }
        n=0;
        over(i,1,3600-1)
        {
            ll now=1;
            while(tot[i]>=now)
            {
                a[++n]=(now*i)%mod;
                tot[i]-=now;
                now<<=1;
            }
            if(tot[i]){
                a[++n]=(tot[i]*i)%mod;
            }
        }
        memset(dp,0,sizeof dp);
        dp[0][0]=1;
        over(i,1,n)
        {
            if(dp[0][0]>1)
                break;
            over(j,0,3600-1)
                dp[1][j]+=dp[0][((j-a[i])%mod+mod)%mod];
            over(j,0,3600-1)
                dp[0][j]+=dp[1][j],dp[1][j]=0;
        }
        if(dp[0][0]>1)puts("YES");
        else puts("NO");
    }
    return 0;
}
发布了160 篇原创文章 · 获赞 150 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/105566359