使数组和能被 P 整除(贪心或者dp)
给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。
现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。
输出
复制6
解析:
就是用它的最大余数进行一个背包dp
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=1e5+100; const int maxx=1e3+100; int a[maxn]; int dp[maxn]; int n,k; int main(){ cin>>n>>k; int sum=0; for(int i=1;i<=n;i++){ cin>>a[i]; a[i]%=k; sum+=a[i]; sum%=k; } memset(dp,0x3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++){ for(int j=sum;j>=a[i];j--){ dp[j]=min(dp[j],dp[j-a[i]]+1); } } cout<<n-dp[sum]<<endl; }
贪心,前缀和做法:
#include<iostream> #include<algorithm> using namespace std; const int maxn=1e5+100; int vis[maxn]; int a[maxn]; int main(){ int n,m; cin>>n>>m; int sum=0; int ans=0; for(int i=1;i<=m;i++){ vis[i]=n; } for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum+=a[i]; sum%=m; ans=max(ans,i-vis[sum]); if(vis[sum]==n) vis[sum]=i; } cout<<ans<<endl; }
1590. 使数组和能被 P 整除
给你一个正整数数组 nums,请你移除最短子数组(可以为 空),使得剩余元素和能被 p 整除。不允许将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
子数组 定义为原数组中连续的一组元素。
示例 1:
输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。
示例 2:
输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。
示例 3:
输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。
示例 4:
输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。
示例 5:
输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0
提示:
1 <= nums.length <= 1e5
1 <= nums[i] <= 1e9
1 <= p <= 1e9
这个题还是用到(sum[r]-sum[l])%k==sum[r]%k-sum[l]%k
总体思路:用哈希表存储前缀和余数,通过余数找待移除的子数组
看不懂没关系往下看就懂了
首先,整个数组的和对p取余数,记录余数mod
然后开始遍历数组,求前缀和
对于前缀和数组,用哈希表记录其对p的余数及索引
也就是key为前缀和子数组的余数sub_mod,val为该前缀和数组的末尾索引i
在求前缀和及其余数的过程中,我们想要找哈希表中是否已经存在和当前余数相差为mod的前缀和子数组
因为如果两个前缀和子数组余数相差为mod,证明这两个数组相差部分构成的子数组就是对p取余数为mod的子数组
把这个子数组去掉,数组的剩余部分的和就可以被p整除
取满足条件的子数组的最小长度
还有一些细节如下,不想看的话可以直接看代码
i. 如果余数mod本身就是0无需移除子数组,返回0
ii. res已经为1的话可以进行剪枝,因为已经是最小的了,但也要注意如果数组长度也是1的话不可以剪枝,因为这样就把整个数组都移除了
iii. res如果为n的话说明没有符合条件的子数组,题目说了不允许将整个数组都移除,因此返回-1
iv. sub_mod可能比mod要小,为了避免target出现负数,target中多加了一个p,也就是target = (sub_mod - mod + p) % p
这个题我也没有理解为什么这样取连续的一段是可以的有待理解
所以说还是自行理解把
typedef long long ll; class Solution { public: int minSubarray(vector<int>& nums, int p) { ll mod=0,sum=0; int n=nums.size(); for(auto x: nums){ mod = (mod + nums[i])%p; sum += nums[i]; } if(mod==0){ return 0; } if(sum<p){ return -1; } unordered_map<int, int> mp; ll s=0; int minlen = INT_MAX; mp[0]=-1; for(int i=0;i<n;i++){ s=(s+nums[i])%p; int z=(s-mod+p)%p; if(mp.find(target) != mp.end()) { minlen = min(minlen, i-mp[target]); } mp[s] = i; } return minlen >= nums.size() ? -1 : minlen; } };