思路与代码均为借鉴:http://www.cnblogs.com/qzqzgfy/p/5621693.html!
模下背包
题目链接:http://codeforces.com/problemset/problem/577/B
本题用到的一个余数性质:(a+b)%m==(a%m+b%m)%m;
题意:输入n、m,序列中包含n个整数,问是否能够找到一个子序列的和使得整除m
思路:根据上面提到的余数的性质,可以用dp,但是复杂度为O(n*m),这样可以优化么?有一个抽屉原理;对于本题的说明就是:对于n>m,那么必定可以找到子序列整除m;
证明:记录各数字的前缀和,则必有取余m有相同的余数的(√),则两个相同的余数之间的序列和即可满足整除m(!)
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
int a[2000005],f[1003],n,m,g[1003];
int main()
{
scanf("%d%d",&n,&m);
if (n>m)
{
printf("YES");
return 0;
}
for (int i=1; i<=n; i++)
scanf("%d",&a[i]);
for (int i=1; i<=n; i++)
{
for(int j=0; j<=m-1; j++)
g[j]=f[j];
for(int j=m-1; j>=1; j--)
if(f[j])
g[(a[i]%m+j)%m]=1;
g[a[i]%m]=1;
for(int j=0; j<=m-1; j++)
f[j]=g[j];
}
if (f[0])
printf("YES");
else
printf("NO");
return 0;
}
题目链接:http://swjtuoj.cn/problem/2383/
题意:输入n,序列包含n个整数,问是否能够找到一个子序列的和为3600或7200;
思路:思路和上题一样一样的,7200%3600==0,所以题意可以转化为是否能够找到一个子序列的和整除m=3600?
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000006;
int a[maxn],g[4003],f[4003];
int main()
{
int n,m;
int T;
scanf("%d",&T);
while(T--)
{
int flag=0;
scanf("%d",&n);
m=3600;
for(int i=0; i<n; i++)
scanf("%d",&a[i]);
memset(g,0,sizeof(g));
memset(f,0,sizeof(f));
if(n>m)
flag=1;
else
{
for(int i=0; i<n; i++)
{
for(int j=0; j<=m-1; j++)
g[j]=f[j];
for(int j=m-1; j>=1; j--)
{
if(f[j])
g[(a[i]%m+j)%m]=1; //已经存在的余数加上新来数a[i]的余数再取余得到的结果,若为0则整除m!
g[a[i]%m]=1;
}
for(int j=0; j<=m-1; j++)
f[j]=g[j];
}
if(f[0])
flag=1;
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}