Codeforces Round 982 (Div. 2)(A~D1题解)

我这个蒟蒻又来发题解了,但是这场比赛前三个还好做,第四个确实有难度的

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值