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

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

传送门(点击传送

A. Add Candies

题意:
  有 m 个包,第 i 个包初始有 i 个糖果,现在可以进行如下操作,当你进行第 j 次操作时,选择一个包 x ,除第 x 个包外每个包增加 j 个糖果,现在让你将每个包的糖果数量操作到相等,请输出你的操作次数以及依次输出每次选择的包。( t 组数据)

思路:
  想法当然是让越靠前的包增加的越多,而且因为是等差数列差一,而且增加也是每次多一个,所以从 1 到 n 依次选择包即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t,n;
    cin>>t;
    while(t--){
    
    
        cin>>n;
        cout<<n<<endl;
        for(int i=1;i<=n;i++){
    
    
            cout<<i<<" ";
        }
        cout<<endl;
    }
    return 0;
}

B. Numbers Box

题意:
  有一个 n 行 m 列的矩阵,现在你可以进行如下操作,每次操作选两个相邻的元素,让这两个元素分别等于这两个元素的值乘以 -1 ,现在让 X 等于矩阵所有的元素之和。现在想知道经过操作后 X 的值最大为多少。找出这个最大值。操作次数不限。( t 组数据)

思路:
  负号可以由相邻的元素进行传递,所以这道题统计原矩阵中有多少个负数即可。若有偶数个负数或者有1个 0 出现,那么这个矩阵我们就可以通过操作让所有的数变为非负数,答案就是所有的元素的绝对值之和。如果有奇数个负数,那么我们可以通过传递让绝对值最小的数为负数即可,答案就是所有元素的绝对值之和减去 2 乘以绝对值最小的数的绝对值。

代码:

#include<bits/stdc++.h>
using namespace std;
int num[105][105];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t,n,m;
    cin>>t;
    while(t--){
    
    
        cin>>n>>m;
        int sum=0,cont=0;
        int minn=0x7fffffff;
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                cin>>num[i][j];
                if(num[i][j]<0){
    
    
                    cont++;
                    num[i][j]*=-1;
                }
                sum+=num[i][j];
                minn=min(minn,num[i][j]);
            }
        }
        if(minn==0){
    
    
            cout<<sum<<endl;
        }else{
    
    
            if(cont%2)cout<<sum-2*minn<<endl;
            else cout<<sum<<endl;
        }
    }
    return 0;
}

C. Knapsack

题意:
  有一个容量为 W W W 的包和 n 个物品,每个物品的质量为 w i w_i wi,现在规定我们这个包至少要放 ⌈ W 2 ⌉ \left \lceil \frac{W}{2} \right \rceil 2W 的物品,最多不能放超过 W W W 的物品,问有可选的方案符合题目要求吗,若有则输出放入物品的个数和从小到大依次输出物品序号。如果没有可选方案则输出 -1 。( t 组数据)

思路:
  贪心,给物品从大到小排序,然后从大物品依次枚举,若放入后总质量大于 W W W则不放该物品,若放入后仍然不达 ⌈ W 2 ⌉ \left \lceil \frac{W}{2} \right \rceil 2W 则放入该物品并继续枚举下一个,若放入后位于 ⌈ W 2 ⌉ \left \lceil \frac{W}{2} \right \rceil 2W W W W 之间则停止,所放入的为一种可选方案。注意判断若所有物品都放入都不达 ⌈ W 2 ⌉ \left \lceil \frac{W}{2} \right \rceil 2W 则不存在可选方案。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
struct node{
    
    
    int loc,v;
}item[maxn];
int ans[maxn];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int  t,n;
    ll w,ww;
    cin>>t;
    while(t--){
    
    
        cin>>n>>w;
        ww=(w+1)/2;
        for(int i=1;i<=n;i++){
    
    
            cin>>item[i].v;
            item[i].loc=i;
        }
        sort(item+1,item+1+n,[](const node &a,const node &b){
    
    return a.v>b.v;});
        ll sum=0;
        int st=0;
        for(int i=1;i<=n;i++){
    
    
            sum+=1ll*item[i].v;
            st++;
            ans[st]=item[i].loc;
            if(sum>w){
    
    
                sum-=1ll*item[i].v;
                st--;
            }
        }
        if(st==0 || sum<ww) cout<<-1<<endl;
        else{
    
    
            cout<<st<<endl;
            sort(ans+1,ans+1+st);
            for(int i=1;i<=st;i++){
    
    
                cout<<ans[i]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}

D. Catching Cheaters

题意:
  有两个序列 A 和 B ,并且有 C 是 A 的子序列, D 是 B 的子序列。现在规定 S ( C , D ) = 4 ⋅ L C S ( C , D ) − ∣ C ∣ − ∣ D ∣ S(C,D)=4·LCS(C,D)-\left | C\right |-\left | D\right | S(C,D)=4LCS(C,D)CD,其中 L C S ( C , D ) LCS(C,D) LCS(C,D)是 C 和 D 的最长公共子序列,求对于序列 A 和 B 的最大 S ( C , D ) S(C,D) S(C,D)是多少。

思路:
  动态规划,我们在最长公共子序列的dp基础上进行延申。不理解最长公共子序列求法的小伙伴可先先看这里:OI-WIKI——动态规划基础。当有 A [ i ] = = B [ j ] A[i]==B[j] A[i]==B[j] 的时候,我们对于原最长公共子序列的状态转移是 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i1][j1]+1 ,那么对于当前这道题,如果 A [ i ] = = B [ j ] A[i]==B[j] A[i]==B[j] 时,我们的 L C S ( C , D ) LCS(C,D) LCS(C,D) 的长度相当于增加了一,对应的 4 ⋅ L C S ( C , D ) 4·LCS(C,D) 4LCS(C,D) 就增加了 4,但是我们对应的要减去的 C 序列和 D 序列的长度也增加了一,所以我们的状态转移方程就是 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 4 − 1 − 1 = d p [ i − 1 ] [ j − 1 ] + 2 dp[i][j]=dp[i-1][j-1]+4-1-1=dp[i-1][j-1]+2 dp[i][j]=dp[i1][j1]+411=dp[i1][j1]+2 。当 A [ i ] = = B [ j ] A[i]==B[j] A[i]==B[j] 时,我们还是从 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j] d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1] 两个状态转移过来,但是因为增加了字母,我们就增加了 C 或者 D 的长度,所以我们的状态转移就不单是取最值,而是取完最值再减一,而且我们不能减为负数,所以最后的结果还要和 0 取最大值,所以我们 A [ i ] = = B [ j ] A[i]==B[j] A[i]==B[j] 时的转移方程是 d p [ i ] [ j ] = m a x ( 0 , m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) − 1 ) dp[i][j]=max(0,max(dp[i-1][j],dp[i][j-1])-1) dp[i][j]=max(0,max(dp[i1][j],dp[i][j1])1)。最后的答案在所有状态中找最大值即可。
  
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
char s1[maxn],s2[maxn];
int dp[maxn][maxn];
int main()
{
    
    
    int n,m,ans=0;
    scanf("%d %d\n",&n,&m);
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    for(int i=1;i<=n;i++){
    
    
        for(int j=1;j<=m;j++){
    
    
            if(s1[i]==s2[j]){
    
    
                dp[i][j]=dp[i-1][j-1]+2;
            }else{
    
    
                dp[i][j]=max(0,max(dp[i-1][j],dp[i][j-1])-1);
            }
            ans=max(ans,dp[i][j]);
        }
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/blaction/article/details/109731924