小苯的钟表显示
根据输入的秒数,分别算出小时,分钟和秒即可
void work()
{
int n ; cin >> n ;
int h = n / 3600 ;
n %= 3600 ;
int f = n / 60 ;
n %= 60 ;
cout << h << " " << f << " " << n << endl ;
}
小苯的输入法
可以通过一个双端队列来维护输入端的左侧和右侧,可以通过设定一个标志cur来判断现在位于左端还是右端,需要注意的是,-不仅仅用于当前那一侧的删除,还可以删除另外一侧
void work()
{
int n;
cin >> n;
string s;
cin >> s;
deque<char>q ;
int cur = 0;
for (int i = 0; i < n; i++)
{
if(s[i] >= 'a' && s[i] <= 'z')
{
if(cur == 0)q.pb(s[i]) ;
else q.push_front(s[i]) ;
}
else if(s[i] == '!')
{
cur = (cur + 1) % 2 ;
}
else
{
if(q.size())
{
if(cur == 0)q.pop_back() ;
else q.pop_front() ;
}
}
}
if(q.size())
{
while(q.size())
{
cout << q.front() ;
q.pop_front() ;
}
cout << endl ;
}
else
{
cout << "Empty" ;
cout << endl ;
}
}
小苯的观景路线
题目要求选出的观景点坐标按照升序排序,所以我们先把得到的数组进行升序排序,按照贪心的思路来说,第一个选出的景点选第一个就好,然后向后遍历,按照题意求解即可
void work()
{
int n ; cin >> n ;
vi a(n) ;
for(int i = 0 ; i < n ; i++)cin >> a[i] ;
sort(all(a)) ;
int mx = 1 ;
int cur = a[0] ;
for(int i = 1 ; i < n ; i++)
{
if(a[i] - cur >= mx)
{
mx++ ;
cur = a[i] ;
}
}
cout << mx << endl ;
}
小苯的序列涂色
由于每次选择的区间可以重叠,所以考虑使用区间dp进行操作,为了简化操作,预先处理一个前缀和。
这道题目dp的关键在于,我们选定前i段区间,然后维护一个最小值,再枚举前j段区间,所以我们的mini其实维护的就是i-j这段区间染色的代价,由于这段区间是只增不减的,所以我们的mini只需要找到最小值就可以了,题目允许同一位置被多次染色
void work()
{
int n ; cin >> n ;
vi a(n + 1 , 0) ;
vi pre(n + 1 , 0) ;
for(int i = 1 ; i <= n ; i++)cin >> a[i] , pre[i] = pre[i - 1] ^ a[i] ;
vi dp(n + 5 , inf) ;
dp[0] = 0 ;
for(int i = 1 ; i <= n ; i++)
{
int mini = inf ;
for(int j = 1 ; j <= i ; j++)
{
mini = min(mini , (pre[i] ^ pre[j - 1]));
dp[i] = min(dp[i] , dp[j - 1] + mini) ;
}
}
cout << min(dp[n] , pre[n]) << endl ;
}
小苯的凝聚区间
注意到答案区间的特点必然是最大值和最小值分布在区间的两端,如果不满足这个条件,区间的长度可以缩短,使凝聚力更大,然后我们按照两种情况对凝聚力公式进行优化:
1、最大值位于左侧,最小值位于右侧
此时公式变形为(M - r) - (m - l) , 也就是一个元素减去自己的坐标
2、最大值位于右侧,最小值位于左侧
此时公式变形为(M + l) - (m + r) , 也就是一个元素加上自己的坐标
于是我们可以想到,遍历整个数组,把我们当前所在的元素当作区间的右端点,然后维护第一种情况的最大值,维护第二种情况的最小值,计算差值并统计答案
void work()
{
int n ; cin >> n ;
vi mp(n) ;
for(int i = 0 ; i < n ; i++)cin >> mp[i] ;
int ans = 0 ;
int mx = mp[0] + 0 ;
int mi = mp[0] - 0 ;
for(int i = 1 ; i < n ; i++)
{
int b1 = mp[i] + i ;
int c1 = mp[i] - i ;
ans = max({ans , c1 - mi , mx - b1}) ;
mi = min(mi , c1) ;
mx = max(mx , b1) ;
}
cout << ans << endl ;
}
小苯的糖果盒
前提:这道题目应用了贪心的一种较为困难的情况,基础模型是将a数组变为b数组,每次可以选择a数组中的两个元素,一个元素+1,一个元素-1,成本是这两个元素的距离,求完成的所消耗的最小代价,解决这类问题,我们需要维护a数组和b数组的前缀和,每次让ans加上a和b前i段前缀和的绝对值。
回到本题:显然这道题目没有明显的b数组,但是我们可以枚举b数组的情况,然后按照前提所提到的公式计算总成本,使得代价最小,考虑到使用dp,因为我们只考虑相邻亮相的关系,所以使用滚动dp进行优化,对于一个dp[i][j],i表示的是当前位置所放置的完全平方数的开根,j表示的是前i个元素的前缀和。
从i = 1枚举到i = n , 明显的,我们分别枚举前面数字的前缀和,再枚举前面所填的数字,这里进行了一次优化,因为考虑到题目要求所有盒子的糖果数量构成一个非递减序列,所以我们所填的数字(这里你可以认为我们所填的数字和前面所填的数字合并进行枚举)只能大于等于前面所填的数字,而mini就负责维护前面所填数字情况的最小代价,之后就是将过去的代价转移到现在的代价,然后dp滚动即可。
最后,我们枚举最后一个所填数字的取值就可以了。
void work()
{
int n;
cin >> n;
vi a(n + 1, 0);
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
a[i] = a[i - 1] + x;
}
int sum = a[n] ;
vii dp(101 , vi(sum + 10 , inf)) ;
dp[0][0] = 0 ;
for(int i = 1 ; i <= n ; i++)
{
vii ndp(101 , vi(sum + 10 , inf)) ;
for(int j = 0 ; j <= sum ; j++)
{
int mini = inf ;
for(int k = 0 ; k <= 100 ; k++)
{
mini = min(mini , dp[k][j]) ;
if(k * k + j > sum)continue ;
int c = abs(j + k * k - a[i]) ;
ndp[k][j + k * k] = min(ndp[k][j + k * k] , c + mini) ;
}
}
dp.swap(ndp);
}
int ans = inf ;
for(int i = 0 ; i <= 100 ; i++)
ans = min(ans , dp[i][sum]) ;
if(ans >= inf)cout << -1 << endl ;
else cout << ans << endl ;
}

京公网安备 11010502036488号