使数组和能被 P 整除(贪心或者dp)

传送门

 

给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。

现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。 

 

 

示例1

输入

复制
7 5
10 3 4 2 2 9 8

输出

复制
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;
    }
};

 

 

posted @ 2020-11-09 19:49  lipu123  阅读(424)  评论(0)    收藏  举报