1、
一个简单的贪心思维题,把一个字符串s变成k次字符串就是把s分为k份,通过修改字符使得每一个子串相同。那么我们只需要记录每一个子串同一位置的字符出现次数,然后把每一个子串同一位置的字符全部修改为出现次数最多的。举个例子:现在有三个子串:abs ,anf ,dav,对于每个子串的第一个位置,a出现了两次,d出现了一次,那么我们就选择把第一个位置的字符全部改为a。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int k;
int cnt[50] ,sub;
char s[100010];
int main()
{
cin >> k;
scanf("%s" ,s + 1);
int len = strlen(s + 1) ,div = len / k;
if (len % k != 0) cout << -1;
else
{
for (int i = 1 ;i <= div ;i ++)
{
memset(cnt ,0 ,sizeof(cnt));
for (int j = i ;j <= len ;j += div) cnt[s[j] - 'a'] ++;//记录每个位置不同字符出现的次数
int maxv = 0;
for (int t = 0 ;t < 26 ;t ++) maxv = max(maxv ,cnt[t]);
sub += maxv;//把每个位置的最大值记录下来,最后一起减去
}
cout << len - sub;
}
return 0;
}
2、
这个题目我们的贪心思想是只管眼前,具体来说就是比如我们从左往右翻硬币,我们就翻左边的连续两个,因为右边的我们已经翻过,再来一遍之前岂不是无用功?从左往右来一遍,从右往左来一遍,看看哪种方案次数最小。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1500
int a[N] ,b[N] ,e[N];//这个题目唯一技巧就是因为只有两种状态,我们就设置为1、2,取反就是3-1 ,3-2就可以了
int ans;
int main()
{
string c ,d;
cin >> c >> d;
for (int i = 0 ;i < c.size() ;i ++)
{
if (c[i] == 'o') a[i] = 1;
else
a[i] = 2;
}
for (int i = 0 ;i < d.size() ;i ++)
{
if (d[i] == 'o') b[i] = 1;
else
b[i] = 2;
}
int t = 0;
memcpy(e ,b ,sizeof(b));
for (int i = 0 ;i < c.size() ;i ++)
{
if (a[i] != e[i])
{
t ++;
e[i] = 3 - e[i];
e[i + 1] = 3 - e[i + 1];
}
}
ans = t ,t = 0;
memcpy(e ,b ,sizeof(b));
for (int i = c.size() - 1 ;i >= 0 ;i --)
{
if (a[i] != e[i])
{
t ++;
e[i] = 3 - e[i];
if (i != 1) e[i - 1] = 3 - e[i - 1];
}
}
ans = min(ans ,t);
cout << ans;
return 0;
}
3、
这个题目的贪心思路并不难,重要的就是细节要考虑全面。我们可以想到:先把负数和正数分开放在两个序列中,并且按照绝对值由大到小排序。如果当前第一个和第二个正数的乘积小于第一个和第二个负数的乘积并且还需要选至少两个数时,我们就选择第一个和第二个负数。其余细节放在注释里:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define N 100010
const ll mod = 1000000009;
ll n ,k ,j[N] ,ne[N] ,ans = 1 ,flag = 0;
bool cmp(ll a ,ll b)
{
return abs(a) > abs(b);
}
int main()
{
cin >> n >> k;
memset(j ,0 ,sizeof(j));
memset(ne ,0 ,sizeof(ne));
int i1 = 0,i2 = 0;
for (int i = 1 ;i <= n ;i ++)
{
ll x;
cin >> x;
if (x > 0) j[++ i1] = x;
else
ne[++ i2] = x;
if (x == 0) flag = 1;//如果数组中有0记得标记一下
}
sort(j + 1 ,j + 1 + i1 ,cmp);
sort(ne + 1 ,ne + 1 + i2 ,cmp);
if (n == k)//此时就没有选择的余地,直接全部乘起来就好
{
for (int i = i1 ;i >= 1 ;i --) ans = ans * j[i] % mod;
for (int i = i2 ;i >= 1 ;i --) ans = ans * ne[i] % mod;
}
else if (i1 == 0 && k % 2)//如果没有正数并且所需要的数字个数为负,那么我们容易想到结果肯定
//为负数,我们就尽量选择绝对值小的负数。
{
for (int i = i2; i >= 1 && i >= i2 - k + 1 ; i--) ans = (ans * ne[i]) % mod;
if (flag) ans = 0;//但是如果数组中有0,我们就直接选,让结果为0,此时是最优选择
}
else{
int f1 = 1,f2 = 1;
for (int i = 1 ;i <= k ;i ++)
{
if ((ne[f2] * ne[f2 + 1] > j[f1] * j[f1 + 1]) && i < k)
{
ans = ans * ne[f2] % mod * ne[f2 + 1] % mod;
i ++;
f2 += 2;
}
else
{
ans = ans * j[f1] % mod;
f1 ++;
}
}
}
if (ans >= 0) cout << ans;
else
cout << 0 - ((0 - ans) % mod);
return 0;
}
4、
这个题目比较难。我们的贪心思路是:把每种巧克力按照价格升序排列,如果价格相同我们就按照保质期降序排列。这样对我们而言,越靠前的巧克力的性价比是越高的。然后我们开始选择巧克力,选择的时候需要注意,尽量把选中的巧克力放在保质期最后一天吃,这样可以利益最大化。代码思路就是先按照上面的排列思路给巧克力排序,再用一个set结构维护没有巧克力吃的日期,每次从中选取小于等于当前巧克力保质期的日期出来买巧克力,最后当巧克力遍历完但是日期没用完说明没有合法方案,如果用完了输出结果就行。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;
#define N 100010
struct node{
int a ,b ,c;
}q[N];
bool cmp(node x ,node y)//排序规则
{
if (x.a != y.a) return x.a < y.a;
return x.b > y.b;
}
set<int> s;
int x ,n;
long long ans = 0;//ll存结果保险,防止数据溢出
int main()
{
cin >> x >> n;
for (int i = 1 ;i <= n ;i ++) cin >> q[i].a >> q[i].b >>q[i].c;
sort(q + 1 ,q + 1 + n ,cmp);
for (int i = 1 ;i <= x ;i ++) s.insert(i);//将所有日期插入
int t = 1;
while (s.size() && t <= n)//日期用完或者巧克力遍历完,任何一种情况就可以退出循环了
{
while (q[t].c && s.size() && *s.begin() <= q[t].b)//如果当前巧克力还有剩下的&&日期没有用完&&剩余日期的最小值小于巧克力保质期,
//我们就可以买一块当前巧克力
{
q[t].c --;
ans += q[t].a;
auto flag = s.upper_bound(q[t].b);//找到第一个大于当前巧克力的日期
flag --;//这个日期前一个就是小于等于当前巧克力日期的最大日期
s.erase(flag);//把这个日期用掉
}
t ++;
}
if (s.size()) cout << -1;
else
cout << ans;
return 0;
}