Codeforces Round #683 (Div. 2, by Meet IT)

A - Add Candies

题面:题面有点绕,总结一下就是有n个长度的数组,数组值跟下标值一样,a[i]=i;然后问你第i次操作,选一个数组中的值,除了它其他都加i,使得数组值相同,问随机一种情况

思路:操作n次,输出1,2,3,4,5 … n即可

B - Numbers Box

题面:给一个n,m的矩阵,有正负,有操作选两个相邻的值,同乘-1,求这个矩阵最大的之和

思路:比赛的时候卡了,因为没想清楚,选了个最小的负数,其实,正解是如果出现奇数个负数,选择最小的abs值,偶数个负数就肯定能全部变正

比赛的时候队友飞快过三题,然后我还在卡B,如果不是队友提醒,可能卡死了

C - Knapsack

题面:给两个数n,w,给n长度数组,求数组里选几个有没有w/2~w范围的值

思路:第一想法背包,但数据n 2e5,w 1e18,好,肯定不行,换一个,排序然后尺取?,于是写了一下,就过了

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define lson(x) x<<1
#define rson(x) x<<1|1
#define mod 998244353
using namespace std;
ll ksm(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans*=a;ans%=mod;
        }
        a*=a;a%mod;
        b>>=1;
    }
}
const int N=2e5+10;
int n,m,t;
ll w;
ll dp[N];
struct node{
    int w,num;
    friend bool operator<(const node a,const node b){
        return a.w<b.w;
    }
}we[N];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%lld",&n,&w);
        int f=1,pos;
        for(int i=0;i<n;i++){
            scanf("%lld",&we[i].w);we[i].num=i+1;
            if(we[i].w*2>=w && we[i].w<=w){
                f=0;pos=i;
            }
        }
        if(!f){
            printf("1\n");
            printf("%d\n",we[pos].num);
            continue;
        }
        sort(we,we+n);
        dp[0]=we[0].w;
        for(int i=1;i<n;i++){
            dp[i]=dp[i-1]+we[i].w;
        }

        for(int i=0;i<n;i++){
            if(dp[i]*2>=w && dp[i]<=w){
                f=0;pos=i;break;
            }
             if(dp[i]*2>=w && dp[i]>w){
                f=2;pos=i;break;
            }
        }
        int pos2=0;
        if(f==2){
            for(int i=0;i<pos;i++){
                while((dp[pos]-dp[i])*2<w){pos++;if(pos>n){break;}}
                if((dp[pos]-dp[i])*2>=w &&(dp[pos]-dp[i])<=w){f=0,pos2=i;break;}
            }
        }
        if(!f){
            printf("%d\n",pos-pos2+1);
            for(int i=pos2;i<=pos;i++){
                printf(i==pos?"%d\n":"%d ",we[i].num);
            }
        }
        else{
            printf("-1\n");
        }
    }
    return 0;
}

D - Catching Cheaters

题面:给n,m两个小于5000的数字,然后输入两个长度为n,m 的字符串s1,s2,取两个子串s1’,s2’长度分别是x,y,求4 * lcs(s1’,s2’)-x-y的最大值

思路:枚举二分贪心wa,因为没考虑n长度字符串的选子串的字母,是个假算法。所以这道题正解就是dp,每次dp题,贪心贪出事。

那么方法就是,每次找到相同的字符,就会对前一个状态+2,如果没有相同,就会有两种选择,移动s1一位,or移动s2一位,每移动一位值就会-1

这样我们就可以列出状态转移方程

s1[i]==s2[j] 的情况比较 dp[i-1][j-1]+2 dp[i-1][j]-1(大于等于0) dp[i][j-1]-1(大于等于0) 哪个大

s1[i]!=s2[j] 的情况比较 dp[i-1][j]-1(大于等于0) dp[i][j-1]-1(大于等于0) 哪个大

然后找dp[i][j]的最大值即可

代码:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define lson(x) x<<1
#define rson(x) x<<1|1
#define mod 998244353
using namespace std;
ll ksm(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans*=a;ans%=mod;
        }
        a*=a;a%mod;
        b>>=1;
    }
}
const int N=5e3+10;
int n,m,t;
char s[N],ss[N];
int dp[N][N];
int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s);
    scanf("%s",ss);
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(s[i]==ss[j]){
                dp[i+1][j+1]=max(dp[i][j]+2,max(max(dp[i+1][j]-1,0),max(dp[i][j+1]-1,0)));
            }
            else{
                dp[i+1][j+1]=max(max(dp[i+1][j]-1,0),max(dp[i][j+1]-1,0));
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
           ans=max(dp[i][j],ans);//cout<<dp[i][j]<<" ";
        }
        //cout<<endl;
    }
    printf("%d\n",ans);
    return 0;
}


posted @ 2020-11-16 17:31  ouluy  阅读(91)  评论(0编辑  收藏  举报