描述 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[i−1][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[i−1][k]==true&& k − a [ i ] k-a[i] k−a[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;
}
完……
在时间的大钟上,只有两个字“现在”。