Codeforces Round #646 (Div. 2) May/31/2020 22:35UTC+8

比赛链接 https://codeforces.com/contest/1363
比赛记录 https://blog.youkuaiyun.com/cheng__yu_/article/details/105395197

A. Odd Selection

在这里插入图片描述
题意:问能否从 n 个数中取 x 个凑成奇数
思路:很简单的题,但直接分类讨论还挺麻烦的。可以循环一遍

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;

int t,n,x;
int a;

int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>x;
        int odd=0,even=0;
        for(int i=1;i<=n;++i)
        {
            cin>>a;
            if(a&1) odd++;
            else even++;
        }

        bool ok=0;
        for(int i=1;i<=odd;i+=2)
        {
            if(i+even>=x&&i<=x)
                ok=1;
        }
        puts(ok?"YES":"NO");
    }
    return 0;
}

分类讨论

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10,mod=998244353;

int t,n,x;
int a;

int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>x;
        int even=0,odd=0;
        for(int i=1;i<=n;++i)
        {
            cin>>a;
            if(a&1) odd++;
            else even++;
        }

        if(odd==0)
        {
            puts("NO");
            continue;
        }
        x--;
        odd--;

        if(x&1)
        {
            if(even==0)
            {
                puts("NO");
                continue;
            }
            even--;
            x--;
        }

        int tt=odd/2;
        x=max(0,x-tt*2);
        if(x==0)
        {
            puts("YES");
            continue;
        }
        x=max(0,x-even);
        if(x==0)
        {
            puts("YES");
            continue;
        }
        puts("NO");
    }
    return 0;
}

D. Guess The Maximums(交互:二分)

在这里插入图片描述
题意:交互题。有一个长度为 n 的数组,有 k 个互不相交的索引的集合,让你构造一个长度为 k 的密码。在构造密码第 i 位时,需要选择数组中不在第 i 个集合内的最大值(集合外的最大值)。你可以向系统询问数组的一些索引,系统会回复你最大值。问最终的密码是多少?

思路:首先 [1,n] 询问一下最大值是多少,然后二分出最大值的位置。然后就没了

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000+10,mod=998244353;

int t,n,k;
int sz,mx;
int visit[maxn];
vector<int> s[maxn];

int query(int n)
{
    printf("? %d ",n);
    for(int i=1;i<=n;++i)
        printf("%d%c",i,i==n?'\n':' ');
    fflush(stdout);
    int res;
    cin>>res;
    return res;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=k;++i)
        {
            cin>>sz;
            s[i].resize(sz);
            for(auto &x : s[i])
                cin>>x;
        }

        mx=query(n);
        int l=1,r=n;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(query(mid)<mx)
                l=mid+1;
            else
                r=mid;
        }

        vector<int> ans;
        for(int i=1;i<=k;++i)
        {
            bool exist=0;
            for(auto j : s[i])
                if(j==l)
                    exist=1;
            if(!exist)
            {
                ans.push_back(mx);
                continue;
            }
            memset(visit,0,sizeof(visit));
            for(auto j : s[i])
                visit[j]=1;
            printf("? %d",n-s[i].size());
            for(int j=1;j<=n;++j)
                if(!visit[j])
                    printf(" %d",j);
            printf("\n");
            fflush(stdout);
            int t;
            cin>>t;
            ans.push_back(t);
        }
        printf("! ");
        for(int i=0;i<ans.size();++i)
            printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
        fflush(stdout);
        string t;
        cin>>t;
    }
    return 0;
}

E. Tree Shuffling(贪心)

在这里插入图片描述
题意:给定一棵树,每个节点有三个值 a、b、c,b和c只能是0或1。一次操作可以选择一个节点u子树上的任意k个节点,花费 a[u]*k,将它们的b值交换。求最小的花费使得每个节点 b 等于 c 。

思路:每个节点想要完成的操作都可以在父节点完成,因此每个节点 v 的 a,应该是所有父节点 a 的最小值。

  • 简单的说,就是当前节点的 a 值是它到根节点路径中所有 a 的最小值。这样把最小值向下累积计算就好了。
  • 不能直接算完的交给父节点计算
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10,mod=998244353;

int n;
int a[maxn],b[maxn],c[maxn];
vector<int> e[maxn];
ll ans;
int cnt[maxn][2];

void dfs(int u,int fa)
{
    a[u]=min(a[u],a[fa]);
    for(auto v : e[u])
    {
        if(v==fa) continue;
        dfs(v,u);
        cnt[u][0]+=cnt[v][0];
        cnt[u][1]+=cnt[v][1];
    }
    if(b[u]!=c[u]) cnt[u][b[u]]++;
    int x=min(cnt[u][0],cnt[u][1]);
    ans+=2ll*a[u]*x;
    cnt[u][0]-=x;
    cnt[u][1]-=x;
}

int main()
{
    scanf("%d",&n);
    int c1=0,c2=0;
    for(int i=1;i<=n;++i)
    {
         scanf("%d%d%d",&a[i],&b[i],&c[i]);
         if(b[i]) c1++;
         if(c[i]) c2++;
    }
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    if(c1!=c2)
    {
        puts("-1");
        return 0;
    }

    ans=0;
    a[0]=2e9;
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}

F. Rotating Substrings(DP)

在这里插入图片描述
题意:给定两个字符串 s 和 t ,每次可以选择一个 l 、r,将 s[ r ] 插入到 s[ l ] 前面。问最少需要操作多少次可以使得 s 和 t 相等

思路

  • 一种思路是用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前在 s 的第 i 个位置,t 的第 j 个位置的最长公共子序列的个数。同时 i <=j,并且 s [1…i ] 是 t [ 1…j ] 的子序列。只有是子序列,才能够在 s[i+1…n]中取字符插入到前面和 t 相等。最后的答案就是, n − d p [ n ] [ n ] n -dp[n][n] ndp[n][n]
    s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j]时,并且满足 s [1…i ] 是 t [ 1…j ] 的子序列时,则可以转移 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 1 ] + 1 , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j]) dp[i][j]=max(dp[i1][j1]+1,dp[i][j])
    s [ i ] ! = t [ j ] s[i]!=t[j] s[i]!=t[j] 时, d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]=max(dp[i][j],dp[i-1][j],dp[i][j-1]) dp[i][j]=max(dp[i][j],dp[i1][j],dp[i][j1])

  • 另一种是题解的思路,用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示匹配了 s 中 i 个字符,匹配到 t 中第 j 个字符所需要的最少插入数
    s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j] 时, d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i ] [ j ] ) dp[i][j]=min(dp[i-1][j-1],dp[i][j]) dp[i][j]=min(dp[i1][j1],dp[i][j])
    s [ i ] ! = t [ j ] s[i]!=t[j] s[i]!=t[j] 时, d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j1]但是需要满足的条件是当前匹配的字符是t[j],那么在后面的 s [ i + 1 … n ] s[i+1…n] s[i+1n] t [ j ] t[j] t[j] 的个数要大于 t [ j + 1 … n ] t[j+1…n] t[j+1n] t [ j ] t[j] t[j] 的个数
    d p [ i ] [ j ] dp[i][j] dp[i][j]也可以用 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]通过从后面找到字符 t [ j ] t[j] t[j] 插入到前面来转移,也就是 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + 1 dp[i][j]=dp[i-1][j]+1 dp[i][j]=dp[i1][j]+1

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;

int tt,n;
int dp[maxn][maxn];
string s,t;

int main()
{
    cin>>tt;
    while(tt--)
    {
        cin>>n>>s>>t;
        string s1=s,t1=t;
        sort(s1.begin(),s1.end());
        sort(t1.begin(),t1.end());
        if(s1!=t1)
        {
            puts("-1");
            continue;
        }

        vector<vector<int> > nums(n+1,vector<int>(27)),numt(n+1,vector<int>(27));
        for(int i=1;i<=n;++i)
        {
            nums[i]=nums[i-1];
            numt[i]=numt[i-1];
            nums[i][s[i-1]-'a'+1]++;
            numt[i][t[i-1]-'a'+1]++;
        }
        for(int i=0;i<=n;++i)
            for(int j=0;j<=n;++j)
                dp[i][j]=-10000;

        dp[0][0]=0;
        for(int i=0;i<=n;++i)
        {
            for(int j=0;j<=n;++j)
            {
                if(i>=1) dp[i][j]=max(dp[i][j],dp[i-1][j]);
                if(j>=1) dp[i][j]=max(dp[i][j],dp[i][j-1]);
                if(i>=1&&j>=1&&i<=j&&s[i-1]==t[j-1])
                {
                    bool ok=1;
                    for(int k=1;k<=26;++k)
                        if(nums[i][k]>numt[j][k])
                            ok=0;
                    if(ok) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
                }
            }
        }
        cout<<n-dp[n][n]<<"\n";
    }
    return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;

int tt,n;
int dp[maxn][maxn];
string s,t;
int sums[30][maxn],sumt[30][maxn];

int main()
{
    cin>>tt;
    while(tt--)
    {
        cin>>n>>s>>t;
        string s1=s,t1=t;
        sort(s1.begin(),s1.end());
        sort(t1.begin(),t1.end());
        if(s1!=t1)
        {
            puts("-1");
            continue;
        }

        memset(sums,0,sizeof(sums));
        memset(sumt,0,sizeof(sumt));

        for(int i=1;i<=n;++i)
        {
            sums[s[i-1]-'a'+1][i]++;
            sumt[t[i-1]-'a'+1][i]++;
        }
        for(int i=1;i<=26;++i)
            for(int j=1;j<=n;++j)
                sums[i][j]+=sums[i][j-1],sumt[i][j]+=sumt[i][j-1];

        for(int i=0;i<=n;++i)
            for(int j=0;j<=n;++j)
                dp[i][j]=10000;

        for(int i=0;i<=n;++i)
            dp[0][i]=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=i;j<=n;++j)
            {
                if(s[i-1]==t[j-1])
                    dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
                else
                {
                    dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
                    int c=t[j-1]-'a'+1;
                    if(sums[c][i]<sumt[c][j])
                        dp[i][j]=min(dp[i][j],dp[i][j-1]);
                }
            }
        }
        cout<<dp[n][n]<<"\n";
    }
    return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;

int tt,n;
int dp[maxn][maxn];
string s,t;

int main()
{
    cin>>tt;
    while(tt--)
    {
        cin>>n>>s>>t;
        string s1=s,t1=t;
        sort(s1.begin(),s1.end());
        sort(t1.begin(),t1.end());
        if(s1!=t1)
        {
            puts("-1");
            continue;
        }

        vector<vector<int> > nums(n+1,vector<int>(27)),numt(n+1,vector<int>(27));
        for(int i=1;i<=n;++i)
        {
            nums[i]=nums[i-1];
            numt[i]=numt[i-1];
            nums[i][s[i-1]-'a'+1]++;
            numt[i][t[i-1]-'a'+1]++;
        }
        for(int i=0;i<=n;++i)
            for(int j=0;j<=n;++j)
                dp[i][j]=10000;

        for(int i=0;i<=n;++i)
            dp[0][i]=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=i;j<=n;++j)
            {
                if(s[i-1]==t[j-1])
                    dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
                else
                {
                    dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
                    int c=t[j-1]-'a'+1;
                    if(nums[i][c]<numt[j][c])
                        dp[i][j]=min(dp[i][j],dp[i][j-1]);
                }
            }
        }
        cout<<dp[n][n]<<"\n";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值