8.27下午
T1
签到题,简单dp,我的做法是三重循环一重循环天数,一重循环当天选择完成的任务,一重枚举上次可能做过的任务,然后用二维dp来存储每i天选择了第j个任务 状态转移方程:
if(j!=k) {
dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i][j]);
}
最后边算边求最大值就行
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int a[maxn][3];
int dp[maxn][3];
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++) {
for(int j=0;j<3;j++) {
cin>>a[i][j];
}
}
int maxx=0;
for(int i=1;i<=n;i++) {
for(int j=0;j<3;j++) {
for(int k=0;k<3;k++) {
if(j!=k) {
dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i][j]);
}
maxx=max(maxx,dp[i][j]);
}
}
}
cout<<maxx;
return 0;
}
T2
首先用一个队列存储原来的排序序列,然后因为栈有先进后出的特性,所以用栈来存储插队的人,然后弹出栈内东西加入新的存储答案的队列,标记进入过队列元素,最后将原本没有插队的放入存储答案的队列就行
#include<bits/stdc++.h>
using namespace std;
stack<int>op;
queue<int>Q;
queue<int>ans;
const int maxn = 1e5+7;
int vis[maxn];
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) {
Q.push(i);
}
for(int i=1;i<=m;i++) {
int a;
cin>>a;
op.push(a);
}
while(!op.empty()) {
vis[op.top()]++;
if(vis[op.top()]==1)
ans.push(op.top());
op.pop();
}
while(!Q.empty()) {
if(vis[Q.front()]<1) {
vis[Q.front()]++;
ans.push(Q.front());
}
Q.pop();
}
while(!ans.empty()) {
cout<<ans.front()<<'\n';
ans.pop();
}
return 0;
}
T3
获得了k瓶汽水,说明在获得k瓶汽水之前,会有k-1个积分,可以兑换
公式:
int num=(k-1)/p;
cout<<1+num*(p-1)+k-1-num*(p)<<'\n';
T4
本题可以通过后面的减所以不能使用dp因为没有后效性
正解由于乘法运算可以达到log级别,但存在大量重复状态,于是我们可以使用记忆化搜索去除重复状态,然后让是2,3,5倍数的数可以直接从它除以2,3,5的因子达到然后再求最小值,而对于有余数的数,对于每一个数,我们都有两种方式
x - x%3的方式-x%3个1
x+3-x%3的方式+3-x%3个1
后面如法炮制即可
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int maxn = 1e18;
map<int,int>mp;
int n,a,b,c,d;
int dfs(int x) {
if (mp.count(x)) {
return mp[x];
}
int ans = x * d;
if (x % 2) {
ans = min(ans, min(dfs((x - x % 2) / 2), dfs((x + 2 - x % 2) / 2)) + a + d);
} else {
ans = min(ans, dfs(x / 2) + a);
}
if (x % 3) {
ans = min(ans, min(dfs((x - x % 3) / 3) + (x % 3) * d, dfs((x + 3 - x % 3) / 3) + (3 - x % 3) * d) + b);
} else {
ans = min(ans, dfs(x / 3) + b);
}
if (x % 5) {
ans = min(ans, min(dfs((x - x % 5) / 5) + (x % 5) * d, dfs((x + 5 - x % 5) / 5) + (5 - x % 5) * d) + c);
} else {
ans = min(ans, dfs(x / 5) + c);
}
mp[x] = ans;
return ans;
}
signed main() {
int t;
cin>>t;
while(t--) {
cin>>n>>a>>b>>c>>d;
mp.clear();
mp[0]=0;
mp[1]=d;
cout<<dfs(n)<<'\n';
}
return 0;
}
T5
40pts
直接暴力枚举每一个区间算中间数
80pts
注意到当一个区间内大于x的数-小于x的数>=0,说明有中位数>=x,所以用前缀和来求出每个区间≥x的数的数量就行
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e5+7;
int a[maxn],sum[maxn];
int n,x;
signed main() {
cin>>n>>x;
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i]>=x) {
a[i]=1;
} else {
a[i]=-1;
}
sum[i]=sum[i-1]+a[i];
}
int ans=0;
for(int l=1;l<=n;l++) {
for(int r=l;r<=n;r++) {
if(sum[r]-sum[l-1]>=0) {
ans++;
}
}
}
cout<<ans<<'\n';
return 0;
}
100pts
基于80pts做法,可以得出sum[r]>=sum[l-1];
此时发现问题可以转化成二维偏序的问题,所以可以用值域树状数组首先枚举r,然后找到<=其值的sum[l-1]的范围
要注意的是:
1:0<=l-1<=n-1,所以别忘记+0
2:有负数的情况,所以要增加一个偏移量
3:lowbit处理0会死循环,所以要让数位偏移
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int maxn = 1e18;
map<int,int>mp;
int n,a,b,c,d;
int dfs(int x) {
if (mp.count(x)) {
return mp[x];
}
int ans = x * d;
if (x % 2) {
ans = min(ans, min(dfs((x - x % 2) / 2), dfs((x + 2 - x % 2) / 2)) + a + d);
} else {
ans = min(ans, dfs(x / 2) + a);
}
if (x % 3) {
ans = min(ans, min(dfs((x - x % 3) / 3) + (x % 3) * d, dfs((x + 3 - x % 3) / 3) + (3 - x % 3) * d) + b);
} else {
ans = min(ans, dfs(x / 3) + b);
}
if (x % 5) {
ans = min(ans, min(dfs((x - x % 5) / 5) + (x % 5) * d, dfs((x + 5 - x % 5) / 5) + (5 - x % 5) * d) + c);
} else {
ans = min(ans, dfs(x / 5) + c);
}
mp[x] = ans;
return ans;
}
signed main() {
int t;
cin>>t;
while(t--) {
cin>>n>>a>>b>>c>>d;
mp.clear();
mp[0]=0;
mp[1]=d;
cout<<dfs(n)<<'\n';
}
return 0;
}