Codeforces Round #570 (Div. 3)

E. Subsequences (easy version)

本题和H题唯一的不同点是数据范围。

你有一个长度为nn的字符串。你可以选择它的任意一个子序列。子序列定义为可以将这个字符串删去若干个字符得到。特别的,空串也是一个子序列。

对于一个长度为mm的子序列,选出它的花费是n-mnm,也就是你需要删掉的字符数量。

你的任务是选出来kk个本质不同的子序列,使得总花费最小。输出这个最小花费。

如果选不出kk个,输出-11。

思路:

因为本题的数据量比较小,所以我们可以采取BFS去模拟整个的过程。

利用bfs与set结合,队列存储当前字符串,每次删除一个字符,若set不存在,更新队列与set。

bfs先从删除少的字符串开始,保证了代价最小效率最优。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <set>
 6 #include <queue>
 7  
 8 #define LL long long
 9 using namespace std;
10 const int MAXN = 200005;
11  
12 int n,k;
13 set<string> st;
14 queue<string> q;
15 string s;
16  
17 int main()
18 {
19     scanf("%d%d",&n,&k);
20     cin >> s;
21     int cnt = 0;
22     q.push(s);
23     st.insert(s);
24     while (!q.empty() && st.size()<k)
25     {
26         string v = q.front();
27         q.pop();
28         for (int i=0;i<v.size();i++)
29         {
30             string nv = v;
31             nv.erase(i,1);
32             if (!st.count(nv) && st.size()+1<=k)
33             {
34                 q.push(nv);
35                 st.insert(nv);
36                 cnt += (n-nv.size());
37             }
38         }
39     }
40     if (st.size()<k)
41         printf("-1
");
42     else
43        printf("%d
",cnt);
44     return 0;
45 }
Ackerman

F. Topforces Strikes Back

n个数中选出来最多3个数,使得这三个数中不存在任意一个数是另一个数的倍数,同时使得这三个数的和最大。

这道题的贪心的想法非常巧妙,看了大佬的博客才知道怎么去贪。 Orz

我们根据我们选出的个数进行讨论:

一、

如果我们只选出了一个数,那么肯定就直接选最大的

二、

如果我们选出了两个数,我们讨论下是不是还一定要选最大的数a呢?答案是肯定的

证明如下:

1、如果有一个数不是a的因数,那么直接用a替换这个数。

2、如果两个数都是a的因数,那么这两个数的和最大是 a/2+a/3 < a,也就是说我们在步骤一中就已经得到最优解了。

所以对于这一种情况,我们只需要选出来最大的数,然后从剩下的数中找到一个不是它的因数的最大的数即可。

三、

如果选出三个数,我们同样还是来看是否一定要选a

可以发现,如果选出来的三个数中有一个或两个数不是a的因数,那么就可以由情况二进行证明,一定要选出来a。

而如果三个数都是a的因数,我们会发现唯一一种特殊情况 a/2 + a/3 + a/5 > a ,我们只要对这种情况进行一次特判就可以了。特判完后就选出最大的a然后一样用第二种情况的方法进行判断

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <set>
 6 #include <queue>
 7  
 8 #define LL long long
 9 using namespace std;
10 int t,n,a[200005];
11 int main() {
12    // freopen("../in.txt","r",stdin);
13     scanf("%d", &t);
14     while (t--) {
15         scanf("%d", &n);
16         int ans = 0;
17         for (int i = 1; i <= n; i++) {
18             scanf("%d", &a[i]);
19             ans = max(ans, a[i]);
20         }
21         sort(a + 1, a + n + 1);
22         int an = a[n];
23         bool flag1 = 0, flag2 = 0, flag3 = 0;
24         for (int i = 1; i <= n; i++) {
25             if (a[i] * 2 == an)flag1 = 1;
26             if (a[i] * 3 == an)flag2 = 1;
27             if (a[i] * 5 == an)flag3 = 1;
28         }
29         if (flag1 && flag2 && flag3)ans = max(ans, an / 2 + an / 3 + an / 5);
30         for (int i=1;i<=n;i++){
31             if (a[n]%a[i] == 0){
32                 a[i] = 2000000000;
33             }
34         }
35        sort(a+1,a+n+1);
36         while (a[n] == 2000000000){
37             n--;
38         }
39         ans = max(ans,an+a[n]);
40         for (int i=1;i<=n;i++){
41             if (a[n]%a[i] != 0){
42                 ans = max(ans,an+a[n]+a[i]);
43             }
44         }
45         printf("%d
",ans);
46     }
47     return 0;
48 }
Ackerman

H. Subsequences (hard version)

本题和E题唯一的不同点是数据范围。

你有一个长度为n的字符串。你可以选择它的任意一个子序列。子序列定义为可以将这个字符串删去若干个字符得到。特别的,空串也是一个子序列。

对于一个长度为m的子序列,选出它的花费是nm,也就是你需要删掉的字符数量。

你的任务是选出来kk个本质不同的子序列,使得总花费最小。输出这个最小花费。

如果选不出k个,输出-1

思路:

这道题的数据范围比E题高了许多,所以如果跟E一样去搜索的话会超时。

这题是个dp题

确定状态:我们不妨设 dp[i][j] 的含义是前 i 个 长度为 j 的字符串的子序列的个数

确定状态转移方程:

第一、如果字符str[i] 第一次出现  dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

                    (取str[i])   (不取str[i])

第二、如果字符str[i] 不是第一次出现  那么我们就找它前面出现的时候 vis[str[i]-'a']

dp[i][j] = dp[i-1][j-1] + dp[i-1][j] - dp[str[i]-'a'-1][j-1]   (减去重复的就好了)

我的代码采取了一个优化,就是如果此时序列已经大于k了我就直接取k就好了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <set>
 6 #include <queue>
 7  
 8 #define ll long long
 9 using namespace std;
10 const int MAXN = 105;
11  
12 ll n,k;
13 char s[110];
14 ll dp[MAXN][MAXN];
15 int vis[28];
16 int main()
17 {
18     scanf("%lld%lld",&n,&k);
19     scanf("%s",s+1);
20     dp[0][0]=1;
21     for(int i=1;i<=n;i++)
22     {
23         int alp=s[i]-'a';
24         dp[i][0]=1;
25         for(int j=1;j<=i;j++)
26         {
27             dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
28             if(vis[alp])
29             {
30                 dp[i][j]-=dp[vis[alp]-1][j-1];
31             }
32             dp[i][j]=min(dp[i][j],k);
33         }
34         vis[alp]=i;
35     }
36     ll cost=0;
37     for(int i=n;i>=0;i--)
38     {
39         cost+=min(dp[n][i],k)*(n-i);
40         k-=min(dp[n][i],k);
41         if(k==0) break;
42     }
43     if(k>0) printf("-1
");
44     else printf("%lld
",cost);
45     return 0;
46 }
47  
Ackerman

 

原文地址:https://www.cnblogs.com/-Ackerman/p/11236089.html