我这个蒟蒻又来发题解了,但是这场比赛前三个还好做,第四个确实有难度的
A. Rectangle Arrangement
题解:当时一看到这个图,然后看周长,一秒钟就想到了了周长转换,其实就是二倍的最大的高+二倍最大的宽,然后写代码2分钟就过了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n;
string s;
int w[200005];
int h[200005];
void solve()
{
int flagh=0,flagw=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i]>>h[i];
flagw=max(flagw,w[i]);
flagh=max(h[i],flagh);
}
cout<<2*(flagh+flagw)<<"\n";
return ;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
solve();
return 0;
}
B. Stalin Sort
思路:俄罗斯人说斯大林排序幽默,我们会发现斯大林排序每次排序后,会将每个数后面,比当前位上的数小的数删除,我们要做到最小次数去完成单调不增的序列的话,假设我们想要i位置的数作为开头,那么我们就要将前面的数删除,后面的比他大的数删除,然后遍历一遍数组就能得到最后的结果
就是跑一遍for循环,每次计算的值,就是当前位置前面数的个数+后面比当前位置大的数的个数,去统计最小的值
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n;
string s;
int a[200005];
void solve()
{
cin>>n;
int minn=0x3f3f3f3f;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
int cnt=0;
for(int j=i+1;j<=n;j++)
{
if(a[j]>a[i])
cnt++;
}
minn=min(minn,cnt+i-1);
}
cout<<minn<<"\n";
return ;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
solve();
return 0;
}
C. Add Zeros
思路:开个map去统计每个点要去变化的数目即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n;
int ans;
int a[1000005];
struct node {
int x;
int y;
} que[1000005];
map<int, int> mp;
bool cmp(const node& a, const node& b) {
return a.x < b.x;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] < n - i + 1) {
que[i] = {0, 0};
} else {
que[i] = {a[i] - (n - i + 1), a[i] - (n - i + 1) + (i - 1)};
}
}
mp.clear();
mp[0] = 1;
sort(que + 1, que + n + 1, cmp);
ans = 0;
for (int i = 1; i <= n; i++) {
if (mp[que[i].x]) {
mp[que[i].y] = 1;
ans = max(ans, que[i].y);
}
}
ans += n;
cout << ans << "\n";
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--) {
solve();
}
return 0;
}
D1. The Endspeaker (Easy Version)
题意:这题还是分析一下题意吧,每次有两种操作
第一种操作:当我们的k小于m的时候,可以将k++
第二种操作,我们可以删除一个a数组的前缀,但是这个前缀的值必须小于b[k]这个值,代价就是m-k
现在问你最小的代价是什么
思路:我们首先想到的就是dp,dp [ i ] [ j ]表示到当用 k=j 的方法时候,到达 i 位置的最小代价
先分析出纯暴力的dp写法
最外层遍历位置,第二层遍历k的值,然当a[i]>b[j]的时候就要break
我们要去寻找最左边的合适的位置P,也就是P到 i 位置上的区间和小于 b[ j ]
然后再遍历一遍m个k值,找出走到P位置上的最小代价是多少
状态转移方程:dp[ i ] [ j ]=min( dp[ i ] [ j ],dp[ P-1 ] [ k ];
但是时间复杂度是O(n*m*max(n,m))
看着这题的数据,百分百会超时的
优化写法
我们可以用二分去优化寻找P位置的过程,因为前缀和是单调的
我们可以用另一个二维数组去统计P位置上的最小代价,然后直接O(1)查询即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n,m;
string s;
int a[300005];
int b[300005];
int pre[300005];
int flag;
int l,r,mid;
void solve()
{
flag=0;
cin>>n>>m;
vector<vector<int>> dp(n+1,vector<int>(m+1,0x3f3f3f3f));
vector<vector<int>> mn(n+1,vector<int>(m+1,0x3f3f3f3f));
fill(dp[0].begin(),dp[0].end(),0);
fill(mn[0].begin(),mn[0].end(),0);
for(int i=1;i<=n;i++)
{
cin>>a[i];
flag=max(flag,a[i]);
pre[i]=pre[i-1]+a[i];
}
for(int i=1;i<=m;i++)
cin>>b[i];
if(flag>b[1])
{
cout<<"-1\n";
return ;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i]>b[j])
break;
l=1,r=i;
while(l<r)
{
mid=(l+r)/2;//mid越往左,区间差值越大
if(pre[i]-pre[mid-1]<=b[j])
{
r=mid;
}
else
{
l=mid+1;
}
}
dp[i][j]=min(dp[i][j],mn[l-1][j]+m-j);
}
for(int j=1;j<=m;j++)
{
mn[i][j]=min(mn[i][j-1],dp[i][j]);
}
}
int minn=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
minn=min(minn,dp[n][i]);
}
cout<<minn<<"\n";
return ;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
solve();
return 0;
}