传送门
题意:给定n个数,求使得若干数之和模5==0&&使得和最大,输出那个最大和
基本的思路就是拿dp做,不过它可以正向做一波,也可以反向做一波,下面来分别介绍一下
解题思路1:dp[i][j]就表示前i项中若干项和模5等于j的最大和的值,明显最后dp[n][0]就是答案,每个数都有选和不选两种方案,明显是个01背包,不过核心部位需要一个小小的处理,请看下面的图和解释
Code:
#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<stack>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9+7;
typedef long long ll;
typedef unsigned long long ull;
int n;
int a[maxn];
ll dp[maxn][5];
int main(){
ll sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
if(sum%5==0)
printf("%lld\n",sum);///总和恰好是5的倍数,直接输出
else{
memset(dp,-0x3f,sizeof dp);///这里的初始化必须将数组开始设为无穷小,不然样例都过不去,
///有可能a[i]是一个很大的数,这就使得刚开始计算就会出错
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=4;j++)
{
int flag=a[i]%5;
if(j-flag>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-flag]+a[i]);
else dp[i][j]=max(dp[i-1][j],dp[i-1][j+5-flag]+a[i]);
}
}
printf("%lld",dp[n][0]);
}
return 0;
}
解题思路2:假设所有数的和为sum,我们可以找出其中若干项的和sum1使得sum1%5=sum%5并且sum1最小,这就使得(sum-sum1)%5==0&&sum最大,此时dp[i][j]表示前i项中若干项的和模5等于j的最小和,明显最后的答案就是sum-sum1,也就是sum-dp[i][sum%5],核心和上面是一致的
Code:
#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<stack>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9+7;
typedef long long ll;
typedef unsigned long long ull;
int n;
int a[maxn];
ll dp[maxn][6];
int main(){
ll sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
if(sum%5==0)
printf("%lld\n",sum);
else{
memset(dp,0x3f,sizeof dp);///和正向做正好相反,初始化为无穷大
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=4;j++)
{
int flag=a[i]%5;
if(j-flag>=0) dp[i][j]=min(dp[i-1][j],dp[i-1][j-flag]+a[i]);
else dp[i][j]=min(dp[i-1][j],dp[i-1][j+5-flag]+a[i]);
}
}
printf("%lld",sum-dp[n][sum%5]);
}
return 0;
}
万事尽可期待! ! !