部分枚举(遥远的银河,LA 3695)

一开始没看清题,以为不一定平行。。。然后就不会了。。。


看了题解写了一遍,发现求某个值最好写在一个函数里,否则有时候代码中间要出一个特例就会很麻烦,甚至要goto。写在函数里一个return就ok了。

然后枚举矩形时最好不要重边,否则一行点就会算两次。重边的情况特判输出就好了。


看了大白书上的代码又模仿了一遍,发现模仿别人写代码很难学到什么东西。因为你会想当然的凭着记忆去写代码,从而导致事实上你根本就没有深入理解这段代码的含义,如果AC了,你也就是抄了一遍而已,如果WA了,你自己从头到尾查一遍,就会发现有一些很低级的逻辑错误,而且还不太容易找到。这都是因为你想当然的觉得对导致的。但是看别人代码还是能学到东西的,一直闷着自己写,代码风格和习惯可能会一直很糟糕。看了别人的代码后,就会灵机一动,然后学到一些优秀的代码技巧和实现方法,就会发现自己的代码有多么啰嗦。


然后是思路。


其实分析下也能大概知道O(n^4)的算法在一秒内只能跑n=100的数据。

然而有多组数据,所以是一定要优化的。


部分枚举是跑不了了,但问题是枚举哪些部分。预处理,记录和维护那些数据。

正常都会想到枚举2~3条边,然后算出答案。

大白书上用的是枚举3条边,然后O(1)算出最大值。

其实能少枚举一维就是很大的优化了,也需要很多信息维护的。


枚举上下边界,然后从左到右枚举右边界,维护最优的左边界,O(1)算出答案。

答案是“算”出来的,而且一定由两部分组成,一部分是只跟右边界有关的量,另一部分是只跟左边界有关的量,然后只跟左边界有关的量就维护成最优值。至于跟上下边界有关的量就暂时当成常量,可以预处理一下。

很容易想到某种题就是给一个数列,求最大子串和,要预处理一个sum数组,sum[i]表示前i个数的和。sum[i]-sum[j-1]就是[j,i]的和。

这题也一样,预处理一个sum数组,sum[k]就代表横坐标小于等于第k条竖边的在上下边界上的点的个数。sum[i]-sum[j-1]就是在j和i边之间的在上下边界上的点的个数。然后再加上左右边界上不再上下边界上的点的个数就是答案。sum[i]-sum[j-1]+on[i]+on[j],把i,j分开那就是sum[i]+on[i]+on[j]-sum[j-1]我们就要维护关于j的最优值,即on[j]-sum[j-1]的最大值。显然从左到右扫一遍极易维护。


我的代码跟大白书上的方法一样,但是跟上面说的略有不同,但大同小异。


我的代码

#include<bits/stdc++.h>
#define maxn 110
#define INF 0X3F3F3F3F
using namespace std;

struct star
{
    int x,y;
    bool operator < (const star& rhs) const
    {
        if(x!=rhs.x) return x<rhs.x;
        else return y<rhs.y;
    }
}S[maxn];

int N;
int kase;
int Y[maxn];
int l;
int lft[maxn],on[maxn],on2[maxn];

int main()
{
    while(scanf("%d",&N)==1&&N)
    {
        for(int i=1;i<=N;i++)
        {
            scanf("%d %d",&S[i].x,&S[i].y);
            Y[i]=S[i].y;
        }
        sort(S+1,S+1+N);
        sort(Y+1,Y+1+N);
        l=unique(Y+1,Y+1+N)-Y;
        if(l<=3)
        {
            printf("Case %d: %d\n",++kase,N);
            continue;
        }
        int ANS=-INF;
        for(int i=1;i<l;i++)
            for(int j=1;j<i;j++)
            {
                int cnt=0;
                int k=0;
                int sum=0;
                int shang=0;
                int zhong=0;
                while(k!=N)
                {
                    shang=0;
                    zhong=0;
                    do
                    {
                        k++;
                        if(S[k].y==Y[i]||S[k].y==Y[j]) shang++;
                        else if(S[k].y>Y[j]&&S[k].y<Y[i]) zhong++;
                    }while(k+1<=N&&S[k+1].x==S[k].x);
                    lft[k]=sum;
                    on[k]=zhong;
                    on2[k]=zhong+shang;
                    sum+=shang;
                    cnt++;
                }
                if(cnt<=2)
                {
                    printf("Case %d: %d\n",++kase,N);
                    goto here;
                }
                int MAX=-INF;
                int PRE=-INF;
                k=0;
                cnt=0;
                while(k!=N)
                {
                    do
                    {
                        k++;
                    }while(k+1<=N&&S[k+1].x==S[k].x);
                    MAX=max(MAX,PRE+lft[k]+on2[k]);
                    PRE=max(PRE,on[k]-lft[k]);
                }
                ANS=max(ANS,MAX);
            }
        printf("Case %d: %d\n",++kase,ANS);
        here:;
    }
    return 0;
}

模仿大白书

#include<bits/stdc++.h>
#define maxn 110
#define INF 0X3F3F3F3F
using namespace std;

struct star
{
    int x,y;
    bool operator < (const star& rhs) const
    {
        if(x!=rhs.x) return x<rhs.x;
        else return y<rhs.y;
    }
}S[maxn];

int N;
int lft[maxn],on[maxn],on2[maxn];
int Y[maxn];

int solve()
{
    sort(S,S+N);
    sort(Y,Y+N);
    int l=unique(Y,Y+N)-Y;
    if(l<=2) return N;
    int ANS=-INF;
    for(int i=0;i<l;i++)
        for(int j=0;j<i;j++)
        {
            int k=0;
            for(int u=0;u<N;u++)
            {
                if(u==0||S[u-1].x!=S[u].x)
                {
                    k++;
                    on[k]=on2[k]=0;
                    lft[k]=lft[k-1]+on2[k-1]-on[k-1];
                }
                if(S[u].y>Y[j]&&S[u].y<Y[i]) on[k]++;
                if(S[u].y>=Y[j]&&S[u].y<=Y[i]) on2[k]++;
            }
            if(k<=2) return N;
            int PRE=-INF;
            for(int u=1;u<=k;u++)
            {
                ANS=max(ANS,PRE+lft[u]+on2[u]);
                PRE=max(PRE,on[u]-lft[u]);
            }
        }
    return ANS;
}

int kase;

int main()
{
    while(scanf("%d",&N)==1&&N)
    {
        for(int i=0;i<N;i++)
        {
            scanf("%d %d",&S[i].x,&S[i].y);
            Y[i]=S[i].y;
        }
        printf("Case %d: %d\n",++kase,solve());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值