http://codeforces.com/contest/1303
D. Fill The Bag
题意:给出了一些物品,每个大小都为2的幂次,有一个已知大小的背包。每个物品可以分裂成两个大小都为原来一半的物品。问最少分裂几次能恰好装满背包?
贪心
从大到小遍历所有物品,如果能放下就直接放,否则判断如果剩下的物品能放满背包,这个物品就不要了。否则就分裂,再把得到的两个物品和其他的一起再继续判断。
如果大物品并不是必须的,那么用小物品分裂放满背包是比分裂大物品更优的。
显然还需要的物品的2进制分布是确定的,现在就是需要把每一位需要的都凑出来,而同样是为了凑出某一位,用大的至少比小的多分裂1次。并且用幂次拼出更大的幂次也不存在需要先分裂再拼的情况,且既然总和足够,任何一位一定都是能拼出来的。所以用小的一定更优。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h> using namespace std;long long t, s, n, m, x, ans;int main () { cin >> t; while (t--) { cin >> n >> m; s = ans = 0 ; multiset<int , greater<int >> f; while (m--) cin >> x, s += x, f.insert (x); if (s < n) puts ("-1" ); else { while (n) { m = *f.begin (); f.erase (f.begin ()); if (m <= n) n -= m, s -= m; else if (s - m < n) ++ans, f.insert (m / 2 ), f.insert (m / 2 ); else s -= m; } cout << ans << endl; } } }
E. Erase Subsequences
题意:有s,t两个字符串,每次操作可以从s串里拿出一个子序列,然后在原串里删掉,接到新串后,问能否在2次操作内拼出t串。
dp
显然需要枚举t串的分割线,分成两部分,再看两部分作为s的子序列能否共存。
假设两部分为 t1,t1
d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 表示到s串第 i i i 位,拼完t1串第 j j j 位,最长能拼出 t2 串第几位。
转移方程为
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + 1 , ( s [ i ] = t 2 [ d p [ i ] [ j − 1 ] ] ) d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] , ( s [ i ] = = t 1 [ j ] ) dp[i][j]=dp[i-1][j]\\
dp[i][j]=dp[i][j-1]+1,(s[i]=t2[dp[i][j-1]])\\
dp[i][j]=dp[i-1][j-1],(s[i]==t1[j])\\
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + 1 , ( s [ i ] = t 2 [ d p [ i ] [ j − 1 ]]) d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] , ( s [ i ] == t 1 [ j ])
第一种情况表示s第 i i i 位啥都不干。
第二种表示用来拼在t2后面。
第三种表示用来接在t1后面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> using namespace std;#define ll long long const int INF = 0x3f3f3f3f ;const int maxn = 2e5 + 10 ;int T, n, m;char s[maxn], t1[maxn], t2[maxn], t[maxn];int dp[410 ][410 ];bool check () { for (int i = 0 ; i <= n; i++)for (int j = 0 ; j <= m; j++)dp[i][j] = -INF; int l1 = strlen (t1 + 1 ), l2 = strlen (t2 + 1 ); dp[0 ][0 ] = 0 ; for (int i = 1 ; i <= n; i++) { for (int j = 0 ; j <= l1; j++) { dp[i][j] = dp[i - 1 ][j]; if (dp[i - 1 ][j] >= 0 && dp[i - 1 ][j] <= l2 && s[i] == t2[dp[i - 1 ][j] + 1 ]) dp[i][j] = max (dp[i][j], dp[i - 1 ][j] + 1 ); if (s[i] == t1[j]) dp[i][j] = max (dp[i][j], dp[i - 1 ][j - 1 ]); } } return dp[n][l1] >= l2; } int main () { cin >> T; while (T--) { bool flg = 0 ; scanf ("%s" , s + 1 ); scanf ("%s" , t + 1 ); n = strlen (s + 1 ); m = strlen (t + 1 ); for (int i = 0 ; i < m; i++) { int j; for (j = 0 ; j <= i; j++)t1[j] = t[j]; t1[j] = 0 ; for (j = i + 1 ; j <= m; j++) t2[j - i] = t[j]; t2[j - i] = 0 ; if (check ()) { flg = 1 ; break ; } } puts (flg ? "YES" : "NO" ); } return 0 ; }