2016.8.1测试解题报告(maxtrix,sum,salesman)

1.螺旋矩阵
这道题前50%的数据暴力就好不解释了;
至于后50%的数据我们找规律。我的方法是:显而易见,按照暴力的数据寻找规律。用c来表示矩阵的所在的层数,我们发现每一层循环的最开始的那块都已知,暴力查询应该查询的点,最坏情况的时间复杂度是O(n*4)。
2.求和
用邻接表存储同颜色的色块,又题意可知,中间块的颜色、序号、权值对答案都没有任何影响,所以只要统计相同颜色的色块中,序号之差为偶数的色块就可以。
3.推销员
根据分析题意我们发现,每一层的结果都包含在上一层查询的结果当中,所以我们只要暴力出第一个点,然后递推出后面的状态就可以了。
考试的时候我用很大精力写了一个There is something I should know:
①第i次选择只需要再选一个。
②每一次的选择,都只需要比较在最远一个已选择的前面的选择,和后面的选择即可。
③维护前面Ai的最大,维护后面Ai+Si*2的最大。
第一条我已经解释过了,分析题意和两个样例很好得出。至于第二条我们进行分析,每次寻找下一个推销的客户的时候我们都要决定,是再往前多走几步(在最远(farst)的右端)还是在回去的路上顺便推销。具体选哪个我们要如第三条所说,维护前面区间即l到farst内的最大点权值,还要维护后面区间即farst到r内的点权值与距离farst的路径长的最大值。
首先,加上几个数,可以等价于多次加上一个数,所以说,我们可以用胜者树做。删除的数,可以等价于将某个数更改为一个很小的数(比如说-INF,这样的话就肯定不会成为最大值)。所以合起来的时间复杂度是O(Nlog2N)。当然,胜者树不要写错,特别是用位运算的时候,就更不要搞错了方向(我就错了好多好多好多好多次)。别忘记在某个新的在当前最远元素后面的元素选了之后还要再加上一些元素进胜者树里。下面我来解释一下代码里面的变量,po[i]即从i之后i到i后面的点的点权值与距离i的路径长的最大值的所在住户位置,p[i]即胜者树,即树顶元素为0到farst的最大点权值(其实作用和堆一样,就是我当时写线段树改残了才写的胜者树)。剩下的好像没什么好说的了=。=
具体的流程大概就是:我先预处理出来从从0到n-1的所有的点到最后一个点的最大的路径长+点权(递推O(1)),然后我会选出第一个点,建树,用选出的第一个点更新胜者树。接下来就是不断选点的过程,注意!每选一个在右边区间里面的点,farst的值就要做出相应的变化,同时向胜者树中添加一些元素,每选一个左边区间里面的点就要从胜者树里面删掉该节点(具体实现也可以是把该节点的值赋为-INF)。更新答案。下面我来粘一波代码。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
const int INF=10000000;
bool used[100010];
int po[100010];
int n;
int p[300040];//shu
int s[100010];
int a[100010];
int lop;
int farst;
int ans;
void update(int,int);
void solve();
int main()
{
    freopen("salesman1.in","r",stdin);
    freopen("salesman.out","w",stdout);
    scanf("%d",&n);
     a[n]=0;
    lop=1;
    for(int i=0;i<n;i++) scanf("%d",&s[i]);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    while(lop<n) lop=lop<<1;
    cout<<lop<<endl;
    po[n-1]=n-1;
    for(int i=n-2;i>=0;i--)
    {
        int g1=s[po[i+1]]*2+a[po[i+1]];
        int g2=s[i]*2 + a[i];
        po[i]=(g1>g2?po[i+1]:i);
    }
    farst=po[0];
    ans=a[po[0]];
    printf("%d\n",s[farst]*2+ans);
    for(int i=0;i<=lop*2;i++) p[i]=n;
    for(int i=0;i<po[0];i++) update(i,a[i]);
    update(po[0],0);
    for(int i=1;i<n;i++) solve();
    return 0;
}
void update(int pos,int x)
{
    int pos2=pos+lop;
    p[pos2]=pos;
    a[pos]=x;
    for(pos2>>=1;pos2!=0;pos2>>=1)
        p[pos2]=(a[p[pos2<<1]]>a[p[(pos2<<1)+1]]?p[pos2<<1]:p[(pos2<<1)+1]);
}
void solve()
{
    int l=p[1];
    if (farst!=n-1)
    {
        int r=po[farst+1];
        if (a[l]>a[r]+(s[r]-s[farst])*2)
        {
            ans+=a[l];
            update(l,0);
        }
        else
        {
            ans+=a[r];
            for(int j=farst+1;j<r;++j)
            {
                update(j,a[j]);
            }
            farst=r;
        }
    }
    else
    {
        ans+=a[l];
        update(l,0);
    }
    printf("%d\n",s[farst]*2+ans);
}

就这样,是不是都懂了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值