Codeforces Round #637

Codeforces Round #637

A.数学

给定k,a,b,c,d

问 区段 k * ( a - b ) 到 k * ( a + b ) 与 区段 c - d 到 c + d 是否有重合部分

简单判断左右端即可

B.前缀和

形如131,1231的各一个山峰3,而1234无

现在给定你一个串,你取其中长度为k的子串,使得它的山峰数越多,如果有多种可能取最左的选择

比如1213,k=3,取前三个有一个山峰,后三个一个没有

思路:

前缀和

首先处理一下哪些是山峰

然后前缀和记录山峰个数,注意当前山峰位置记录的个数不能加1,因为它若作端不算山峰,应该考虑,区段内的山峰数,即下一位才加1

同样减去前缀的时候也要注意是额外要加1处理

AC:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 200005
#define minn -105
#define ll long long int
#define ull unsigned long long int
ll a[maxn],pre[maxn];
ll max(ll a,ll b)
{
    return a>b?a:b;
}
void solve()
{	
	int n,k;
	cin>>n>>k;
	ll curmax=0;
	memset(pre,0,sizeof(pre));
	for(int i=0;i<n;i++)
	{	
    	cin>>a[i];
    	pre[i]+=pre[i-1];
    	if(i>=2&&a[i-2]<a[i-1]&&a[i-1]>a[i])pre[i]++;
    	if(i>=k-1)curmax=max(curmax,pre[i]-pre[i-k+2]);
	}
	for(int i=k-1;i<n;i++)
    {
        if(pre[i]-pre[i-k+2]==curmax)
        {
            cout<<curmax+1<<" "<<i+2-k<<endl;
            return;
        }
    }
}
int main()
{
    int t;
    cin>>t;
    for(int i=0;i<t;i++)
        solve();
    return 0;
}

C.找规律

原题地址:https://codeforces.ml/contest/1341/problem/C

这题题意很复杂,读懂其实不难,这里懒得翻译

思路:

一开始r数组为12345...k(k+1)...n

你放1,除了最右端的位置,其他位置k你只要一放,

队列就成了 12345...(k+1)(k+1)...n

那么你的2就只能放k+1了,故2若出现在1右侧则1必须与2相邻

同理3也是...

那么就可以形成形如这样的队列:

895671234

如果队列不长这样那么就不行

怎么判别?

思路一:从1开始往右扫,直到触界,1234,取5对应的位置,567,取8的位置,89

思路二:利用性质——只要比当前元素大1的在当前元素的右边,那么他们必定相邻

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 100005
#define minn -105
#define ll long long int
#define ull unsigned long long int
#define uint unsigned int
inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'||ch>'9')last=ch,ch=getchar();
    while(ch>='0'&&ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}
int a[maxn],pos[maxn];
void solve()
{
    int n=read();
    int cur=0;
    bool ok=1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        pos[a[i]]=i;
    }
    for(int i=1;i<n;i++)
    {
        if(pos[i]<pos[i+1]&&pos[i+1]-pos[i]!=1)ok=0;
    }
    if(ok)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
	}
int main()
{
    int _t;
    _t=read();
    while(_t--)solve();
    return 0;
}

D.DP

原题链接:https://codeforces.ml/contest/1341/problem/D

题意,给你n个数用火柴棒表示,现在给你额外k个火柴棒,你添上去使得新数最大

每个位置火柴棒的有无均采用01,最先给出的是最高位,注意k,n<=2000

思路:动态规划

首先注意到这个范围才2000,比较小

其次我们想如果对于55,我们只有一根火柴,那么肯定优先添加给高位成为95

所以我们DP要从低位开始,我们数组dp[第几个数][到现在一共用了几根],用它表示第几个数在使用了一共几根下对应的最大数字

状态转移方程

if(dp[i-1][j-k]有解)dp[i][j]=opt{save[i][k],0<=k<=min(7,j)}

当dp[i-1][j-k]存在值,即低位均有可行解时,我们掏了k根给第i号数,而save[i][k],指的是当第i号数它接受了k个火柴最优值的情况,把这个值给了dp

所以我们要先预处理save[i][k]:枚举每个数i(0->n-1),枚举数字(0->9)(如果你枚举给他k个火柴的话会很麻烦),与0-9每一位进行对比,记录可添加火柴数的情况(非法情况记为-1)

DP过程:注意结果要求输出,所以还要记录你具体给每个数多少根,只要每个dp[i][j]对应记录一个pre[i][j],表示当前方案下第i号数取给了多少根,得到dp[i-1][m]后往回找、输出即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 2005
#define minn -105
#define ll long long int
#define ull unsigned long long int
const string num[10]= {"1110111", "0010010", "1011101", "1011011", "0111010",
                    "1101011", "1101111", "1010010", "1111111", "1111011"};
int n,m;
int dp[maxn][maxn],pre[maxn][maxn],save[maxn][8];
string s[maxn];
void init()
{	
	for(int i=0;i<n;i++)
	    for(int j=0;j<8;j++)
	        save[i][j]=-1;
	for(int i=0;i<n;i++)
	    for(int j=0;j<=m;j++)
	        dp[i][j]=pre[i][j]=-1;
}
void deal()
{
    for(int i=0;i<n;i++)
        for(int j=0;j<10;j++)
        {
            bool ok=1;
            int cnt=0;
            for(int bit=0;bit<7;bit++)
     	   {
                if(s[i][bit]=='1'&&num[j][bit]=='0')
                {
                    ok=0;
                    break;
                }
                if(s[i][bit]=='0'&&num[j][bit]=='1')
                    cnt++;
            }
            if(ok)save[i][cnt]=max(save[i][cnt],j);
        }
}
void DP()
{
    for(int i=0;i<=min(7,m);i++)
    {
        dp[0][i]=save[0][i];
        pre[0][i]=i;
    }
    for(int i=1;i<n;i++)
        for(int j=0;j<=min(m,(i+1)*7);j++)
            for(int k=0;k<=min(j,7);k++)
            {
                if(save[i][k]==-1||dp[i-1][j-k]==-1)continue;
                if(save[i][k]>dp[i][j])
                {
                    dp[i][j]=save[i][k];
                	pre[i][j]=k;
                }
            }
    if(dp[n-1][m]==-1)
    {
        cout<<-1<<endl;
        return;
    }
    for(int i=n-1;i>=0;i--)
    {
        cout<<dp[i][m];
        m-=pre[i][m];
    }
    cout<<endl;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
        cin>>s[i];
    reverse(s,s+n);
    init();
    deal();
    DP();
    return 0;
}
原文地址:https://www.cnblogs.com/et3-tsy/p/12768601.html