7.3Music Problem(鸽巢原理,dp)

11 篇文章 0 订阅

第3节 Music Problem
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:00
In 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


题意:是否存在数列中某些数相加之和为3600的倍数

思路:
1.当n>=3600时一定存在(鸽巢原理/抽屉原理)
证明:当存在n+1个数时,一定存在至少两个数模n后相等
若有n个数,对这n个数取前缀和,得到的n个数必不相等,则模n后要么取尽[0,n-1],的数,取0的那个前缀和符合条件,若没有取尽[0,n-1],则必有两个取模后相等,这俩前缀和相减得到的序列和必然是3600的倍数
2.对于n<3600的情况,dp
dp[i]=1或0,表示余数为i的情况是否存在
当dp[j]=1时, dp[(j+a[i])%3600]=1也成立,最后看dp[0]是否为1
注意要用vector 转存
最后再更新dp[a[i]]


#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
int t,n;
int a[N];
int dp[4000];//dp[i]=1或0,表示余数为i的情况是否存在
//转移依据,当dp[j]=1时, dp[(j+a[i])%3600]=1也成立,最后看dp[0]是否为1 
vector<int>res;//必须要用vector转存,若 直接dp[(j+a[i])%3600]=1,则会影响后面的dp[j] 
//如:a[i]=2,dp[1]=1,则dp[3]=1,然而直接存会出现dp[5]=1,dp[7]=1......这种情况 
bool ac(int n)
{
	memset(dp,0,sizeof(dp));
	if(n>=3600) return true;//前缀和中至少又两个模 3600后相等,将这两个前缀和相减即为3600的倍数 
	for(int i=1;i<=n;i++)//扫描每一个数 
	{
		res.clear();
		if(dp[0]) return true; 
		for(int j=1;j<3600;j++)
		{
			if(dp[j]) 	res.push_back((j+a[i])%3600);	
		}
		int len=res.size();
		for(int i=0;i<len;i++)
		{
			dp[res[i]]=1;
		}
		dp[a[i]]=1;//最后再更新dp[a[i]] 
	}
	if(dp[0]) return true;
	else return false;
}

int main()
{
	
//	freopen("test.txt","r",stdin);
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			a[i]%=3600;
		}
		if(ac(n)) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}
/*
3
2
5400 1800
3
2000 1000 3000
3
2000 3000 1600


*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值