解题思路:
先来说一下题意,n个数,每个数前面随意加上+-号(当然+号等于没加),然后所有的数相加,判断和是否为k的整数。
两种方法,一个是使用dfs深搜,将每种情况都进行尝试,时间复杂度2^n,能过本网站数据,数据量大的时候会超时。
还有一种是dp,这是最好的方法。
dfs代码:
#include <bits/stdc++.h> using namespace std; int p[10007]; int n,m,vis=0; void dfs(int k) { if(k==n) {//全部更改过位置 int z=0; for(int i=0; i<n; i++) { z+=p[i]; } if(z%m==0)vis=1;//如果可以整除 return; } else { for(int i=0; i<2; i++) { if(i&1) { dfs(k+1); } else { p[k]*=-1; dfs(k+1); p[k]*=-1; } } } } int main () { cin>>n>>m; for(int i=0; i<n; i++) { cin>>p[i]; } dfs(0); cout<<(vis?"YES":"NO"); return 0; }
来详细说一下dp代码,dp[i][j]为前i个数是否可以被j整除,1表示可以,0不可以。
因为最后所有的值是要相加在一起的,所以最后的值依赖于前面的情况。
以第二个数为例,如果想要第一个数加上第二个数后sum对k取余值为j,那么第一个数p1只有两种情况,一个是p1==j-a2,一个是p1==j+a2(这里对p2前加(-)号)
推广到所有的数身上,想要加到第i个数sum%k的值为j,那么加到第i-1个数时必须满足两种情况(j-pi或者j+pi)中的一个,这样在加上第i个数或减去第i个数后和sum才为j,j%k=j(j<k)。
根据(a+b)%k=(a%k+b%k)%k,可以先进行取余后处理,一方面可以减少数组开销另一方面确保j<k。
这样我们就可以写递推式了,dp[i][j]代表的是前i个数是否可以被j整除,dp[i][j]就和(dp[i-1][(j+p[i])%k]、dp[i-1][(j-p[i])%k])有关了,只有这两个中的一个为真,dp[i][j]才为真。
递推式:dp[i][j]=dp[i-1][(j+p[i]+k)%k] || dp[i-1][(j-p[i]+k)%k]
两个做或运算,有真则真,注意这里分别+k,是为了防止j-p[i]为数组下标为负数的情况。
根据同余 (j-p[i]+k)%k=(j-p[i])%k+k%k=(j-p[i])%k
dp代码:
#include <bits/stdc++.h> using namespace std; int p[10007]; bool dp[10007][107]; int n,k; int main () { cin>>n>>k; for(int i=1; i<=n; i++) { cin>>p[i]; p[i]%=k; } dp[0][0]=1;//边界,记为1 for(int i=1; i<=n; i++) { for(int j=0; j<k; j++) { dp[i][j]=dp[i-1][(j+p[i]+k)%k]||dp[i-1][(j-p[i]+k)%k]; } } cout<<(dp[n][0]?"YES":"NO");//dp[n][0]表示前n个数相加,对k取余余数为0,即:前n个数相加有可以整除k的情况 return 0; }
dp的核心是dp[i][j]为前i个数是否可以被j整除,这是本题的核心。
以上。
0.0分
1 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
发表评论 取消回复