Codeforces&&DP总结

Codeforces&&DP总结

一.Codeforce:
4.16Codeforces Round #715 (Div. 2)
这次时第一次扣分得比赛!!!!太难受了
A. Average Height
这次A题非常之简单,只要把输入的数字的奇数和偶数分开然后输出就得到答案(奇数在前偶数在后或者偶数在前奇数在后),没有一点难度
AC代码:

#include <iostream>
 
using namespace std;
int a[2005],b[2005];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        int i,j,k=1;
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n;i++)
        {
            if(a[i]%2==0)
            {
                b[k]=a[i];
                k++;
            }
        }
        for(i=1;i<=n;i++)
        {
            if(a[i]%2==1)
            {
                b[k]=a[i];
                k++;
            }
        }
        for(i=1;i<k-1;i++)
            cout<<b[i]<<" ";
        cout<<b[k-1]<<endl;
    }
    return 0;
}

这是比赛时写的代码,其实可以更简单,题目太简单了,就不再改进了
B. TMT Document
这个题其实没有想象中的难,但是当时思考的方向不对,一直在想着使用for循环然后找到M后再怎么操作,然后再用erase()删除该字符串的字符,只要两串字符就可以意识到这个思路行不通,(ex:TMTTMT,TMTMTT,如果遇到M后从两边查找T,第一个案例行不通,如果是从M到两边查找T,第二个案例行不通),但是并没有转换思路,因为这是第一次比赛的时候一开始思路就错的情况,没有想着改思路,到最后也没有做出来,现在一想,实在是太亏了,一条路走不到终点,就应该换一条路试试,如果道路是错误的,越往下走,做的都是无用功
正确思路:应该是使用for循环从一边开始进行,如果遇到M的数量大于T的时候,输出NO,再用for循环从另一边开始,同样遇到M的数量大于T的时候,输出NO,从另一边开始的原因的案例也很容易找到(ex:TTTMMTTTM,这时候如果只有从头尾的一次for循环,那么不会输出NO,就需要从另一边再使用一次for循环)
AC代码

#include <bits/stdc++.h>
using namespace std;
string a;
int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    cin>>t;
    while(t--)
    {
        int flag=0,n,summ=0,sumt=0;
        cin>>n>>a;
        for(int i=0;i<a.size();i++)
        {
            if(a[i]=='T')
                sumt++;
            else summ++;
            if(summ>sumt){flag=1;break;}
        }
        summ=sumt=0;
        for(int i=a.size()-1;i>=0;i--)
        {
            if(a[i]=='T')
                sumt++;
            else summ++;
            if(summ>sumt){flag=1;break;}
        }
        if(summ*2!=sumt) flag=1;
        if(flag==1) cout<<"NO\n";
        else cout<<"YES\n";
    }
    return 0;
}

其实也可以只需要一次for循环就能解决问题,只是输出NO的条件再加一条

if(sumt-summ>n/3)
cout<<"NO\n";

(ex:TTTMMTTTM,这个判断其实也挺容易理解,如果一边的T和M数量的差值大于总子数列的数目,可以理解为前面的一个T需要一个M与其配对,可是如果T-M的数量大于总序列数的时候,就说明TMT的后面的T与M不能完成配对)
4.12Educational Codeforces Round 107 (Rated for Div. 2)
这是打Codeforces以来出的最多题的一场比赛,而且还是Div2的比赛,不过Educational版本的,题目也非常简单
A. Review Site
我感觉A题就是锻炼读题的,读题准确,五分钟出代码,题目很长,意思很简单,就是求支持票和不知道投啥票的总和
AC代码:

#include <iostream>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,i,sum=0;
        cin>>n;
        int a[55];
        for(i=1;i<=n;i++){
            cin>>a[i];
            if(a[i]==1||a[i]==3)
                sum++;}
        cout<<sum<<endl;
    }
    return 0;
}

B. GCD Length
这个题真是给我开了个玩笑,花了四五十分钟想出来的思路,结果只需要两行代码就能解决
这是比赛时AC的代码:

#include <iostream>
 
using namespace std;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        int m=1,n=1,minn=1,maxx=1;
        for(int j=1;j<a;j++)
            n=n*10;
        for(int j=1;j<b;j++)
            m=m*10;
        for(int j=1;j<=c;j++)
        {
            maxx=maxx*10;minn=maxx;
        }
        maxx--;
        minn/=10;
        int temp=gcd(n,m);
        for(;;temp=gcd(n,m)){
            if(temp<=maxx&&temp>=minn)//这里要使用temp变量,在之前赋值为gcd(n,m),如果直接使用gcd(n,m)会超时
            {
                cout<<n<<" "<<m<<endl;
                break;
            }
            m++;
        }
 
    }
    return 0;
}

看了别人的AC代码,就像对我说,这题也需要敲代码?
别人AC代码:

#include<bits/stdc++.h>
using namespace std;
int a,b,c,t;
int main(){
	cin>>t;
	while(t--){
		cin>>a>>b>>c;
		int x=pow(10,a-1)+pow(10,c-1);
		int y=pow(10,b-1);
		cout<<x<<" "<<y<<endl;
	}
}

没错,这就需要理解好题意了,其实一开始我把第一个数赋值为10是非常不确定的,因为不知道10对应的另外一个数是多少,如果很大的话,很可能超时,但是当看完别人的代码,我发现,是我多虑了,其实CF的比赛Div3和Div2的前几道题都是可以用非常简单的代码进行实现的,思路也挺简单,就看能不能想起来了
C. Yet Another Card Deck
一开始写代码的时候是非常不敢写的,因为给的数值太大了,思路中需要使用二重循环才能够解决问题,所以到最后也是本着搏一搏的心态去提交的,思路很简单,找到一个数,把该数提到最前面,把该数前面的数往后挪一个位置,输出该数原始位置
AC代码

#include <iostream>
 
using namespace std;
int t[300005],a[300005];
int main()
{
    int n,q,temp;
    int i,j;
    cin>>n>>q;
    for(i=1;i<=n;i++)
        cin>>a[i];
    for(i=1;i<=q;i++)
        cin>>t[i];
    for(i=1,j=1;j<=q;i++)
    {
        if(a[i]==t[j])
        {
            if(j!=q)
            {
            cout<<i<<" ";
            temp=a[i];
            for(int k=i;k>1;k--)
                a[k]=a[k-1];
            a[1]=temp;
            j++;
            i=0;
            continue;
            }
            if(j==q)
                {cout<<i;break;}
        }
 
    }
    return 0;
}

4.11Divide by Zero 2021 and Codeforces Round #714 (Div. 2)

A. Array and Peaks
思路:根据Peaks的数量,从大到小,按照2,4,6,8…的位置顺序进行插入,剩下的依次排序
AC代码:

#include <iostream>
 
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int a[105]={0};
        int n,k,i,j,num=0;
        cin>>n>>k;
        a[1]=1;
        if(k==0)
        {
            for(i=2;i<=n;i++)
                a[i]=i;
                for(i=1;i<=n;i++)
            {
                if(i!=n)
                   cout<<a[i]<<' ';
                       else cout<<a[i]<<endl;
            }
        }
        else
        {
 
        for(i=2,j=0;i<=n;i+=2,j++)
        {
            if(i!=n)
            {
               a[i]=n-j;
               num++;
            }
            if(num==k)
                break;
        }
        if(num==k)
        {
            for(i=2,j=2;i<=n;i++)
            {
                if(a[i]==0)
                {
                    a[i]=j;
                    j++;
                }
            }
            for(i=1;i<=n;i++)
            {
                if(i!=n)
                   cout<<a[i]<<' ';
                       else cout<<a[i]<<endl;
            }
        }
        else if(num<k) cout<<"-1"<<endl;
        }
 
    }
    return 0;
}

二.DP总结
题目网址:https://vjudge.net/contest/430905
G.
这个题提交了17遍,在AC后又提交了12遍,只为了研究明白一个问题,定义全局变量数组和局部变量数组的问题,这个问题很容易导致Runtime Error(ACCESS_VIOLATION)
对于全局变量和局部变量,这两种变量存储的位置不一样。对于全局变量,是存储在内存中的静态区(static),而局部变量,则是存储在栈区(stack)。
这里,顺便普及一下程序的内存分配知识:
C语言程序占用的内存分为几个部分:
1、堆区(heap):由程序员分配和释放,比如malloc函数
2、栈区(stack):由编译器自动分配和释放,一般用来存放局部变量、函数参数
3、静态区(static):用于存储全局变量和静态变量
4、代码区:用来存放函数体的二进制代码
在C语言中,一个静态数组能开多大,决定于剩余内存的空间,在语法上没有规定。所以,能开多大的数组,就决定于它所在区的大小了。

在WINDOWS下,栈区的大小为2M,也就是210241024=2097152字节,一个int占2个或4个字节,那么可想而知,在栈区中开一个int[1000000]的数组是肯定会overflow的。我尝试在栈区开一个2000000/4=500000的int数组,仍然显示overflow,说明栈区的可用空间还是相对小。所以在栈区(程序的局部变量),最好不要声明超过int[200000]的内存的变量。

而在静态区(我没有在网上找到它具体大小的数据,但是可以肯定比栈区大),用vs2010编译器试验,可以开2^32字节这么大的空间,所以开int[1000000]没有问题。

总而言之,当需要声明一个超过十万级的变量时,最好放在main函数外面,作为全局变量。否则,很有可能overflow。
参考:https://blog.csdn.net/cjsycyl/article/details/9230429?utm_source=app&app_version=4.5.8
写了这么多,现在说一下思路吧
思路:数组小,可以先不考虑超时问题,二重循环用来给移动dp,二重循环里面套二重循环,在能量的基础上给每一个可以到达的位置进行增值,暴力,其实在数组只有100的时候就可以想到这个办法
AC代码:

#include <iostream>
#include <string>
using namespace std;
int main()
{
  int t;
  cin>>t;
  while(t--)
 {
  int i,j,n,m;
  int a[105][105];
  int dp[1115][1115];
  memset(dp,0,sizeof(dp));
  cin>>n>>m;
  for(i=1;i<=n;i++)
      for(j=1;j<=m;j++)
          cin>>a[i][j];
        dp[1][1]=1;
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            {
            if(dp[i][j]){
            int d=a[i][j];
            int x,y;
  for(x=0;x<=d;x++)
 {
      for(y=0;y<=d;y++)
     {
          if(x+y==0)
             continue;
          if(x+y>d)
             break;
            int di=x+i,dj=y+j;
          if(x+i<=n&&y+j<=m)
             dp[di][dj]+=dp[i][j];
             dp[di][dj]%=10000;
     }
 }
}
            }
              cout<<dp[n][m]%10000<<endl;
 }

}

Y.
思路:dp[i][t],t表示字符串末尾的数字大小,i控制字符串长度,dp[i][j],j表示一组数中的最大值,用for循环进行扫描,j等于一个数的时候t每次增加一个j,运行关系式

dp[i+1][t]=(dp[i+1][t]+dp[i][j])%mod;

其实就是让结尾的数每次增加一个j,确保前面的数除以后面的数没有余数
AC代码:

#include <iostream>
#define mod 1000000007//第一次使用预处理宏感觉还不错嘿嘿嘿
using namespace std;
int dp[2005][2005];
int main() {
int n,k,sum=0;
	cin>>n>>k;
	dp[0][1]=1;
	for(int i=0;i<=k;i++)
		for(int j=1;j<=n;j++)
			for(int t=j;t<=n;t+=j)
				dp[i+1][t]=(dp[i+1][t]+dp[i][j])%mod;
	for(int i=1;i<=n;i++)
		sum=(sum+dp[k][i])%mod;
	cout<<sum;
}

三.感悟
这个星期的重心偏向于Codeforces的比赛,4.10,4.11,4.12连续三天打的虽然累,但是收获也不小,参悟透了一点CF的题目,这几天打比赛,让我突然想到了一句话,当你排除所有的不可能,最后一个结果就是可能,我称之为,“穷举法”,虽然很笨,但是在做题的时候可以尝试一下,嘿嘿嘿…
线性DP的题目也告一段落了,最近学习了局部DP,感觉差别不大,但是线性DP的题都没很懂,一下又换到局部DP,危机感涌向心头…ACM也逐渐融入生活,慢慢成了生活里的一部分,选ACM的室友在努力的同时,也会促使自己努力,跟着舍友一起熬夜打比赛,一起竞争或者一起合作,合作中竞争,竞争中合作,或许,这就是ACM的快乐吧

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值