表达式整除

描述 Description
24点这个游戏好多人都玩过,就是给你4个数,添加相应的运算符,是否可以得到结果是24.
小x在玩了很多遍这个游戏之后,想把这个游戏给改变一下。
给你n个整数,在n个整数间,只能添加+和- 两种运算符。
比如:给你4个整数:17,5,-21,-15。
你可以构成下列8个表达式:
17+5+(-21)+15=16
17+5+(-21)-15=-14
17+5-(-21)+15=58
17+5-(-21)-15=28
17-5+(-21)+15=6
17-5+(-21)-15=-24
17-5-(-21)+15=48
17-5-(-21)-15=18
现在的问题是:给你n个数,能够构成一个表达式,让这个表达式的值能被某个给定的数整除。
上面的例子中:可以被7整除,但是不能被5整除。
输入格式 Input Format
第一行一个整数,表示有k组测试数据。
接下来对于每组测试数据有两行:
第一行是两个整数n和x(1<=n<=10000, 2<=x<=100), n 表示数列中整数的个数;x就是需要你判断的这个数列是否能被x 整除。
第二行是n个用空格隔开的整数,表示表达式中的n歌整数,每个数的绝对值都不超过10000。
输出格式 Output Format
包含k行,每行表示一组数据的结果。
如果能被x整除,输出"Divisible",如果不能,则输出 “Not divisible” ,


思路

在看着一道题的时候,我想的是把前面的所有的可能组成的数字都存下来,这样的话,我就可以一个一个把备用状态进行更新。但是,让我没想到的是,这个算法出现了非常神奇的错误:(如果有人知道这种情况是为什么,请告知博主) 初步判断是内存炸了,但他显示的又不是MLE……
在这里插入图片描述
在我请教老师之后,老师告诉了我为什么会这样:确实是内存出了问题。在这里插入图片描述
之后,我们考虑正解

我们可以把这个问题变形:对于每一个数字而言,我们把它看成是一个节点,那么这个节点会引出两条边,一条是+ver,另外一条是-ver(ver是该节点的下一个节点的值),也就是说,我们可以把问题变成是:从1号节点出发,判断是否有一条路径满足该路径上的值%x==0

我们可以发现,这道题的并不满足DP的最优化原理的性质:在每一个子问题最优的情况下,原问题会最优。

所以,我么考虑增加状态:设 b o o l bool bool f [ i ] [ j ] f[i][j] f[i][j]表示在当前第i个数字此时的余数为j的情况是否可行。

那么我们只需要找到 f [ i − 1 ] [ k ] = = t r u e f[i-1][k]==true f[i1][k]==true&& k + a [ i ] k+a[i] k+a[i]% x = = j ∣ ∣ x==j|| x==j f [ i − 1 ] [ k ] = = t r u e f[i-1][k]==true f[i1][k]==true&& k − a [ i ] k-a[i] ka[i]% x = = j x==j x==j即可。


code

#include<bits/stdc++.h>
using namespace std;

const int nn=10003;

int T, n, m;
int a[nn], f[nn][203];

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}

int main()
{
	T=read();
	while(T--)
	{
		n=read(), m=read();
		memset(f,0,sizeof(f));
		for(int i=1;i<=n;++i)	a[i]=read()%m;
		f[1][a[1]+100]=true;
		for(int i=2;i<=n;++i)
			for(int j=0;j<=203;++j)
				if(f[i-1][j])
				{
					int x=(j-100+a[i])%m;
					int y=(j-100-a[i])%m;
					f[i][x+100]=true, f[i][y+100]=true;
				}
		if(f[n][100])	puts("Divisible");
		else puts("Not divisible");
	}
	return 0;
}

完……

在时间的大钟上,只有两个字“现在”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值