zoukankan      html  css  js  c++  java
  • 8-21模拟赛解题报告

    呜呜呜~评测机炸锅!模拟赛爆零!!( >﹏<。)


    T1.chess

    题目大意:给出一个(n imes n)黑白棋盘,求将其中(k imes k)区域的黑棋变成白棋后,全是白棋的行数和列数之和。

    这题(n,kle 2000),所以很显然只能使用(O(n^2))算法,所以算每一个点的贡献就成了一个不错的方式。

    我们先从行来看,如果一行中有贡献,必要的条件就是两边的黑棋的距离必须小于k,而如果我们只看最两边的黑棋的话,会发现,有贡献的区域(即(k imes k)矩阵左上角在这个区域时答案会加一)是成矩形。所以我么就可以使用二维差分数组,计算答案。具体如下:

    设第(i)行最左与最右的黑棋的纵坐标设为(l,r),所以矩阵左上角的纵坐标至少应为(r-k+1),最多不能超过(l),横坐标至少应为(i-k+1),最多不能超过(i)。所以设差分数组为(sum[i][j])

    [sum[i-k+1][r-k+1]+1,sum[i-k+1][l+1]-1 ]

    [sum[i+1][r-k+1]-1,sum[i+1][l+1]+1 ]

    然后算一算(sum)的前缀和取最大值就好了。

    要注意的一点就是(i-k+1)等可能会小于(1),要与(1)(max),以及可能会出现一行没有黑棋的情况,要特判。

    小结:

    解题关键:想到计算贡献与差分维护

    芝士点:差分

    其实也还是一道思维题啦!

    手起,码落:

    #include<bits/stdc++.h>
    #define re register
    using namespace std;
    const int N=2005;
    int n,k,s,sum[N][N],ans;
    char ch[N][N];
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for(re int i=1;i<=n;i++)scanf("%s",ch[i]+1);
    	for(re int i=1,l,r,yor;i<=n;i++)
    	{
    		yor=0,l=1,r=n;
    		for(re int j=1;j<=n;j++)
    		{
    			if(ch[i][j]=='B')
    			{
    				yor=1;
    				l=max(l,j-k+1);
    				r=min(r,j);
    			}
    		}
    		if(yor==0)s++;
    		if(r<l)continue;
    		sum[max(i-k+1,1)][l]++;
    		sum[max(i-k+1,1)][r+1]--;
    		sum[i+1][l]--;
    		sum[i+1][r+1]++;
    	}
    	for(re int i=1,l,r,yor;i<=n;i++)
    	{
    		yor=0,l=1,r=n;
    		for(re int j=1;j<=n;j++)
    		{
    			if(ch[j][i]=='B')
    			{
    				yor=1;
    				l=max(l,j-k+1);
    				r=min(r,j);
    			}
    		}
    		if(yor==0)s++;
    		if(r<l)continue;
    		sum[l][max(i-k+1,1)]++;
    		sum[r+1][max(i-k+1,1)]--;
    		sum[l][i+1]--;
    		sum[r+1][i+1]++;
    	}
    //	for(re int i=1;i<=n;i++)
    //	{
    //		for(re int j=1;j<=n;j++)printf("%d ",sum[i][j]);
    //		puts("");
    //	}
    	for(re int i=1;i<=n;i++)
    		for(re int j=1;j<=n;j++)
    			sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1],ans=max(ans,sum[i][j]);
    	printf("%d",ans+s);
    	return 0;
    }
    

    T2.sequence

    一句话题意:求一个序列分成若干块后的最大极差之和。(极差为最大值减最小值

    这题看似简单,其实!也并不难。。

    直接上吧,(f[i])表示前(i)个数的最大极差之和。首先,显而易见的一点是:在一个单调序列中间的节点绝对不会是决策点,因为选择端点一定优于中间节点。所以我们就可以把中间节点去掉,直接与(f[i-1])(f[i-2])达到(O(1))转移啦!

    要注意这题因为某些不知明的原因,用了一种输入量也是极大的方式生成数据。但代码中还是正常输入。

    小结:

    解题关键:想到单调序列中间节点非决策点而优化时间。

    芝士点:动态规划

    感觉最近的思维题好多呀!

    手起,码落:

    #include<bits/stdc++.h>
    #define re register
    #define max(x,y) ((x)>(y)?(x):(y))
    #define MOD 1073741824
    using namespace std;
    inline long long read()
    {
    	re long long x=0,f=1;
    	re char ch=getchar();
    	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f*=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    	return x*f;
    }
    const int N=10000005,M=500005;
    int len;
    long long n,aa[N],ans,a[N],f[N];
    bool vis[N];
    int main()
    {
    	n=read();
    	for(re int i=1;i<=n;i++)aa[i]=read();
    	vis[1]=vis[n]=1;
    	for(re int i=2;i<n;i++)
    	{
    		if(aa[i]>=aa[i-1]&&aa[i]>=aa[i+1])vis[i]=1;
    		if(aa[i]<=aa[i-1]&&aa[i]<=aa[i+1])vis[i]=1;
    	}
    	for(re int i=1;i<=n;i++)if(vis[i])a[++len]=aa[i];
    	for(re int i=2;i<=len;i++)f[i]=max(f[i-1],f[i-2]+abs(a[i]-a[i-1]));
    	printf("%lld",f[len]);
    	return 0;
    }
    
  • 相关阅读:
    203. Remove Linked List Elements
    python练习小程序
    五十音练习小软件
    CocosCreator生命游戏
    一个swift下载程序
    用excel做一幅像素画
    翻译一篇SpiderMonkey GC的文章
    unity-3d拼图游戏
    NDK编译Python2.7.5
    git命令简图
  • 原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13574123.html
Copyright © 2011-2022 走看看