Codeforces Round 876 (Div. 2)

Codeforces Round 876 (Div. 2)

A.The Good Array

解题思路

比赛的时候不需要考虑什么最优,看到数据范围,直接枚举 i i i,取前 i i i 个需要多少 1 1 1 和后 n − i n-i ni 个需要多少个 1 1 1 的和的最大值,也就是 ⌈ i k ⌉ + ⌈ n − i k ⌉ \left \lceil \frac{i}{k} \right \rceil+\left \lceil \frac{n-i}{k} \right \rceil ki+kni,这个式子表示。

这样枚举就包括了所有情况。

AC_Code

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#pragma warning(disable : 4996)
#define int long long
#define se second
#define fi first
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;

const int maxn=2e5+10,inf=0x3f3f3f3f;
signed main()
{
	ios;
	int t;
	cin >> t;
	while (t--)
	{
		int n,k;
		
		cin >> n>>k;
		int ans = 0;
		for (int i = 1; i <= (n-1)/2+1; i++)
		{
			ans = max((i - 1) / k + 1 + (n - i - 1) / k + 1, ans);
		}
		cout << ans << endl;
	}
}

B.Lamps

解题思路

将所有输入的数对按照第一个数为第一关键词从小到大排序, b b b 的从大到小为第二关键词,那么可以找出规律,数 a a a 1 1 1 的数对可以选 1 1 1 个, a a a i i i 的数对可以选 i i i 个,因为这样选当选 a = i + 1 a=i+1 a=i+1 的时候, a = i a=i a=i 的灯会关闭, 4 4 4 就又变成了 0 0 0,所以只要从从右到左选择就好了。

AC_Code

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#pragma warning(disable : 4996)
#define int long long
#define se second
#define fi first
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
 
const int maxn=2e5+10,inf=0x3f3f3f3f;
struct A
{
	int a, b;
};
signed main()
{
	ios;
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		cin >> n;
		vector<A>a(n+2);
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i].a >> a[i].b;
		}
		sort(a.begin() + 1, a.end()-1, [](A x, A y)
			{
				if (x.a == y.a)return x.b > y.b;
				else return x.a < y.a;
			});
		int ans = 0;
		int x = 0;
		vector<int>st(n + 1,0 );
		int z = 0;
		for (int i = 1; i <= n; i++)
		{
			if (a[i].a > x && z != a[i].a)x = 0;
			if (a[i].a > x)ans += a[i].b,x++,z=a[i].a;
		}
		cout << ans << endl;
		
	}
}

C.Insert Zero and Invert Prefix

解题思路

合法的序列最后一个字符一定不是1,因为假如是1的话,那么是不可能有一种方案让最后一个从0变成1的,先考虑一下这样造序列有什么序列是很容易造出来的,考虑能不能用简单的序列造出复杂的序列,可以想到 00000 00000 00000 是最简单的序列,不需要一次变换,一直带 0 0 0 后面插入 0 0 0 就好了,其次就是 11000 11000 11000,就是一些 1 1 1 加上一些 0 0 0 的序列,这种序列稍微复杂一点,先在0后面插入 4 4 4 0 0 0 然后在 2 2 2 后面插入 1 1 1 0 0 0,那么这样就可以造出这个序列,这两种方式我们都是可以轻松模拟出来的,并且所有合法的序列都可以用这两种序列拼接出来,所以我们目的达到了,这个过程要反着实现,才不会影响后面的序列,所以要先插入 1 1 1 序列的长度,再插入 0 0 0。注意输出的时候要反着输出。

AC_Code

#include<iostream>
#include<algorithm>
#include<vector>
#define int long long
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;

signed main()
{
    ios;
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        vector<int>a(n + 1);
        for (int i = 1; i <= n; i++)cin >> a[i];
        if (a[n])
        {
            cout << "NO" << endl;
            continue;
        }
        int tt = 0, hh = -1;
        int x=0;
        int flag = 0;
        vector<int>ans(n + 1);
        int cnt = 0;
        for (int i = 1; i <= n; i++)
        {
            if (a[i] == 1)x++;
            else
            {
                ans[++cnt] = x;
                while (x)
                {
                    ans[++cnt] = 0;
                    x--;
                }
            }
        }
        cout << "YES" << endl;
        for (int i = ans.size()-1; i >>= 0; i--)cout << ans[i] << ' ';
        cout << endl;
    }
}

D.Ball Sorting

解题思路

题意就是有两种操作,一种是将0插入到序列中,没有代价,另一种是将一个与0相邻的元素插入到任意位置代价为1,题目问,小于等于k次操作一前提下,求最小的代价,$1\le k\le n $。

这里有一些元素是不需要动的,也就是不需要花费代价移动,并且很容易发现,这些元素是严格单调递增的。

然后这些元素会将这个序列分割成若干个区间,这些区间的元素都是需要移动的,由于不动的元素的存在,所以每一段就至少需要一个k,那么这个区间里的所有元素才可以移动。

所以这个问题就转化为了,在这个序列中,找到一个上升子序列,也就是不移动的元素组成的序列,这个子序列会将这个序列划分为不超过 k k k 段,求这个满足条件的最长的子序列的长度。

考虑使用动态规划算法,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示序列中前 i i i 个元素以 i i i 结尾并且划分为 j j j 段的最小代价,这里类似于求最长上升子序列,不过这里需要枚举一下段数,

图片描述

状态状态转移方程, d p [ i ] [ j ] = m i n { a [ i − 1 ] < a [ i ] ? d p [ i − 1 ] [ j ] : ∞                                  m i n k = 1 n ( a [ k ] < a [ i ] ? d p [ k ] [ j − 1 ] + ( i − k − 1 ) : ∞ ) dp[i][j]=min\left\{\begin{matrix} a[i-1]<a[i]? dp[i-1][j]:\infty \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ min_{k=1}^n(a[k]<a[i]?dp[k][j-1]+(i-k-1):\infty )\\ \end{matrix}\right. dp[i][j]=min{a[i1]<a[i]?dp[i1][j]:                                mink=1n(a[k]<a[i]?dp[k][j1]+(ik1):)

AC_Code

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        vector<int>a(n+2,0);
        a[n+1]=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        vector dp(n + 2, vector<int>(n + 1,0x3f3f3f3f));
        dp[0].assign(n+2,0);
        for(int i=1;i<=n+1;i++)
        {
            for(int j=0;j<=n;j++)
            {
                if(a[i]>a[i-1])dp[i][j]=dp[i-1][j];
                if(j)
                for(int x=0;x<i;x++)
                {
                    if(a[x]<a[i])dp[i][j]=min(dp[i][j],dp[x][j-1]+i-x-1);
                }
            }
        }
        for(int i=1;i<=n;i++)cout<<dp[n+1][i]<<' ';
        cout<<endl;
    }
    return 0;
}

E.Decreasing Game

解题思路

题意:给定一个正整数数组a长度至多 300 300 300,元素值至多 300 300 300

甲乙二人玩游戏,每次从a中各选一个正数 a [ i ] , a [ j ] , i ≠ j a[i],a[j],i\ne j a[i],a[j],i=j,甲先选。设d=min(a[i],a[j]),把a[i]和a[j]都减少d。如果有人无法操作,游戏结束。无法操作的人输掉游戏,另一个人获胜。

首先确定谁必赢。然后你扮演必胜的人,和评测机玩这个游戏。

通过分析发现,假设 s s s a a a 数组所有元素的和,那么如果 a a a 可以被分成和相同的两份,那么就后手必赢。现在证明一下,假如这组数被分成了两份,那么先手选择了一个数,后手就可以选择另一个数,最后就只有全部 0 0 0,然后只要这样后手就必胜。然后分成相同的两份可以使用01背包。

AC_Code

#include<bits/stdc++.h>
using namespace std;
int n,a[999],f[1<<20],s,x,y,v[999],d;
int main(){
	cin>>n,f[0]=1;
	for(int i=1;i<=n;i++)
		cin>>a[i],s+=a[i];
	for(int i=1;i<=n;i++)
		for(int j=s;j>=a[i];j--)
			if(!f[j]&&f[j-a[i]])
				f[j]=i;
	if(s&1||!f[s/2]){
		cout<<"First"<<endl;
		while(1){
			x=1;
			while(!a[x])
				x++;
			cout<<x<<endl,cin>>y;
			if(!y)
				return 0;
			d=min(a[x],a[y]),a[x]-=d,a[y]-=d;
		}
	}
	cout<<"Second"<<endl;
	x=s/2;
	while(x)
		v[f[x]]=1,x-=a[f[x]];
	while(1){
		cin>>y,x=1;
		if(!y)
			return 0;
		while(!a[x]||v[x]==v[y])
			x++;
		cout<<x<<endl,d=min(a[x],a[y]),a[x]-=d,a[y]-=d;
	}
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值