背包问题
货币拼凑问题
P5020 [NOIP2018 提高组] 货币系统
考虑大面值的货币若能被小面值的货币凑出,那么这个大面值货币就一定是多余的
将货币大小又小到大排序,从大面值的货币一直枚举到小的货币,再嵌套枚举一层比当前货币面值小的货币,利用完全背包思想判断大面值的货币能否被小面值的货币所表示,若能,必要货币数量减去 1 1 1 即可
1 2 3 4 5 6 7 8 9 10 11 12 void Solve () { int ans = n; sort (a + 1 , a + 1 + n); for (int i = n; i >= 1 ; i --){ memset (f, 0 ,sizeof (f)); for (int o = 1 ; o < i; o ++) for (int j = a[o]; j <= a[i]; j ++) f[j] = max (f[j], f[j - a[o]] + a[o]); if (f[a[i]] == a[i]) ans --; } printf ("%d\n" , ans); }
其实这种问题最快的方法是筛法,即将从 1 1 1 到最大货币面值枚举货币面值,若该货币面值可以被表示,则枚举所有的货币,依次加上,将这种方式表示成功的面值进行标记,最后统计未被标记的即可
时间复杂度为 O ( m a x ( a i ) n ) O(max(a_i)n) O ( m a x ( a i ) n )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 memset (f, 0 , sizeof (f)); memset (a, 0 , sizeof (a)); scanf ("%d" , &n); for (int i = 1 ; i <= n; i ++){ scanf ("%d" , &a[i]); f[a[i]] = 2 ; } sort (a + 1 , a + 1 + n); for (int i = 1 ; i <= a[n]; i ++){ if (!f[i]) continue ; for (int j = 1 ; j <= n; j ++) if (i + a[j] <= a[n]) f[i + a[j]] = 1 ; else break ; } int ans = 0 ; for (int i = 1 ; i <= a[n]; i ++) if (f[i] == 2 ) ans ++; printf ("%d\n" , ans);
区间 DP
涂色问题
P4170 [CQOI2007]涂色
这类问题只需要在朴素区间 DP 过程中加上端点相同颜色转移方程,即当区间左右端点相同时,本就可以在之前就一次性涂过,枚举断点前判断即可
1 2 3 4 5 6 7 for (int len = 2 ; len <= n; len ++) for (int l = 1 ; l + len - 1 <= n; l ++){ int r = l + len - 1 ; if (a[l] == a[r]) f[l][r] = min (f[l + 1 ][r], f[l][r - 1 ]); for (int k = l; k <= r; k ++) f[l][r] = min (f[l][r], f[l][k] + f[k + 1 ][r]); }