算法分析
优化:
-
1、由于第
i
层只用到第i - 1
层,于j - 1
严格小于j
,并且需要用到上一轮的数据,因此需要j
从大到小遍历到1
,因此可以优化到二维 -
2、贪心:余数相同时取数组较大的数,因此分别将余数是
0
到k - 1
的最大3
个数挑出来处理即可,最多挑出3 * 1000
个数进行处理
时间复杂度 $O(3k * 3 * k)$
参考文献
蓝桥杯辅导课
Java 代码
import java.util.*;
public class Main{
static int N = 1010;
static int INF = 0x3f3f3f3f;
static int n,m;
static int[][] f = new int[4][N];
static LinkedList[] a = new LinkedList[N];
static int mod(int x,int y)
{
return (x % y + y) % y;
}
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
m = scan.nextInt();
for(int i = 0;i <= m;i ++)
{
a[i] = new LinkedList<Integer>();
}
for(int i = 1;i <= n;i ++)
{
int x = scan.nextInt();
a[x % m].add(x);
}
for(int i = 0;i <= 3;i ++) Arrays.fill(f[i], -INF);
f[0][0] = 0;
for(int i = 0;i <= m;i ++)
{
Collections.sort(a[i]);
Collections.reverse(a[i]);
//选择前3大
for(int u = 0;u < Math.min(3, a[i].size());u ++)
{
int x = (int) a[i].get(u);
//从大到小
for(int j = 3;j >= 1;j --)
{
for(int k = 0;k < m;k ++)
f[j][k] = Math.max(f[j][k], f[j - 1][mod(k - x,m)] + x);
}
}
}
System.out.println(f[3][0]);
}
}
为什么要初始化为负无穷啊,0怎么过不了呢
有些状态是不合法的所以我们需要把开始不合法的状态定义为相对负无穷,这样的话他转移的状态的结果也是负无穷,不会对最后结果影响
那其实只要是负数不就行了吗,为什么-1,-2那些过不了
如果是负数的话你加的数是比这个数大的,你没把状态定义为不可转移或者说转移之后依旧是负无穷的话就还是会被转移
为什么贪心要选最大的三个数
不然时间复杂度会爆
我的意思是相同余数直接取最大的一个不就行吗,为什么要取三个
答案的三个数可能是模K同余的,即可能取在同一类里
哦,原来是这样,谢谢,如果三个数模 K 的余数加起来等于 K的倍数,那么这三个数的和就是K的倍数吧
是滴