20151227-训练题R2

E-乱搞 题目链接


题解:

        一开始想错了,误以为只要找到等候时间最长的那一层,再加上楼层数,就是结果了,但实际上
还是太年轻。AC思路是,暴力搜每层,用数组记录每层所需的最大时间,再取最大数为结果;那么如何记
录每层所需的最大时间?
        每层所需的最大时间是:max(s-a[i].f,a[i].t)+a[i].f,即电梯从该楼层下到地面所需的时间与
该楼层的等待时间的较大者,加上该楼层数,即结果。


#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

struct passenger
{
    int f,t;
} a[105];

int main()
{
    int n,s,maxt=0;
    scanf("%d%d",&n,&s);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&a[i].f,&a[i].t);
        maxt=max(maxt,max(s-a[i].f,a[i].t)+a[i].f);
    }
    printf("%d\n",maxt);
    return 0;
}



--------------------------------------------------------------------------------------------------------------------

F-乱搞 题目链接


题解:

        超时:试图顺序读串b中长度为lena的子串,再遍历子串和串a,看是否相同,不同则sum++,但结
果就是超时了,因为串a最长可以到200000;
        AC思路:用一个long long型的二维数组c,且第二维大小为2,遍历串b,c[][0]保存串b中0的个数,
c[][1]保存串b中1的个数;最后再遍历串a,如果ai=‘1’,那么ans+=c[lenb-lena+i][0]-c[i][0]+(a[i]==b[i])?0:1;
否则,ans+=c[lenb-lena+i][1]-c[i][1]+(a[i]==b[i])?0:1 。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[200005],b[200005];
long long c[200005][2];

int main()
{
    scanf("%s%s",a,b);
    long long ans=0;
    int lena=strlen(a),lenb=strlen(b);
    for(int i=0;i<lenb;i++)
    {
        if(i>0)
        {
            c[i][0]=c[i-1][0];
            c[i][1]=c[i-1][1];
        }
        c[i][b[i]-'0']++;
    }
    for(int i=0;i<lena;i++)
    {
        if(a[i]=='1')
            ans+=(c[lenb-lena+i][0]-c[i][0]+(a[i]==b[i]?0:1));
        else
            ans+=(c[lenb-lena+i][1]-c[i][1]+(a[i]==b[i]?0:1));
    }
    printf("%I64d\n",ans);
    return 0;
}


------------------------------------------------------------------------------------------------------------------

G-DP 题目链接


题解:

        有n个灯塔从左往右排列成一行,第i个灯塔对应位置ai、能量bi;

        第i个灯塔被激活,它就会毁掉左边bi距离内的所有灯塔(不会毁自己),且激活是从右往左每次激活一个;

        然后可以增加一个灯塔在最右边,可以是任意的位置ai(只要在最右就好)和能量bi,让被毁的灯塔数最少;

       

        用数组a[]保存位置pos的能量,数组dp[]保存假设位置pos的灯塔被激活所剩下的灯塔数目,nmax记录最远位置,res记录最大的剩余灯塔数,

//记得数组a和数组dp都要先清零;

        关于dp的状态转移:如果有灯塔在第一个位置,那么记录dp[0]为1,即灯塔本身,然后从1到nmax遍历,

如果位置i灯塔的能量a[i]大于等于它所在位置,说明它左边所有的灯塔都会被毁,只剩下自己,即dp[i]=1

而若小于,那么从位置i-a[i]-1到位置i之间的灯塔都会被毁,那么只剩下位置i-a[i]-1的剩余灯塔数+1(+1是位置i灯塔),即dp[i]=dp[i-a[i]-1]+1。

        最后,最小的毁灭数则是n-res。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1000000
int a[MAXN+5],dp[MAXN+5];

int main()
{
    int n,pos,nmax=0,res=0;
    scanf("%d",&n);
    memset(a,0,sizeof(a));
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++)
    {
        scanf("%d",&pos);
        scanf("%d",&a[pos]);
        nmax=max(nmax,pos);
    }
    if(a[0]>0) dp[0]=1;
	res=max(res,dp[0]);
    for(int i=1;i<=nmax;i++)
    {
        if(a[i]==0)
            dp[i]=dp[i-1];
        else
        {
            if(a[i]>=i)
                dp[i]=1;
            else
                dp[i]=dp[i-a[i]-1]+1;
        }
        res=max(res,dp[i]);
    }
    printf("%d\n",n-res);
    return 0;
}


---------------------------------------------------------------------------------------------------------------

H-DP 题目链接


题解:

        从高往低滑,且只能向上下左右四个相邻点滑去,求最大滑行长度。

        二维数组a[][]保存各点高度,二维数组m[][]保存以点[i][j]为起点往下滑行的最大长度(开始要将所有数组元素置为-1),再用递归函数dp返回所有点中的最大长度;

之所以要用数组m保存数据,是防止超时,保存好了,以便下一次递归时直接返值,而不是又要进行计算。

        递归函数dp(int i,int j):注意进行判断和return还有递归的顺序,先判断该点求过值没,如果有求过,就返回该点的值(防TLE);不然的话,就开始对该点的上下左右四个方向进行对比(还要判断该方向上有没有点、以及该点的高度是否小于原点高度),取最大值;最后才写如果该点==-1(即还没求过值),则该点对应的长度m[i][j]为1的判断语句,在该判断语句外最后写return 点i,j对应的最大长度。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a[105][105],m[105][105];
int r,c;

int dp(int i,int j)
{
    if(m[i][j]!=-1) return m[i][j];
    if(i-1>=0&&a[i-1][j]<a[i][j]) m[i][j]=max(m[i][j],dp(i-1,j)+1);
    if(i+1<r&&a[i+1][j]<a[i][j]) m[i][j]=max(m[i][j],dp(i+1,j)+1);
    if(j-1>=0&&a[i][j-1]<a[i][j]) m[i][j]=max(m[i][j],dp(i,j-1)+1);
    if(j+1<c&&a[i][j+1]<a[i][j]) m[i][j]=max(m[i][j],dp(i,j+1)+1);
    if(m[i][j]==-1) m[i][j]=1;
	return m[i][j];
}

int main()
{
    int maxlen=0;
    scanf("%d%d",&r,&c);
    memset(m,-1,sizeof(m));
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            scanf("%d",&a[i][j]);
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            maxlen=max(maxlen,dp(i,j));
    printf("%d\n",maxlen);
    return 0;
}


-----------------------------------------------------------------------------------------------------------------

I-DP LCIS 题目链接


题解:

        输出最长公共子序列的长度。

        状态转移方程:

               maxlen[i][j]=maxlen[i-1][j];
                if(s1[i]>s2[j]&&nmax<maxlen[i-1][j])
                    nmax=maxlen[i-1][j];
                if(s1[i]==s2[j])
                    maxlen[i][j]=nmax+1;

    maxlen[i][j]二维数组(先清零)保存 到串1第i和串2第j个元素为止 的lcis长度,nmax记录当前的最大lcis长度,

先拿串1的第1个元素 与 串2 的元素依次比较,若串1的第1个元素>串2的第j个元素【保证了是上升序列】,且nmax小于maxlen[0][j]记录的lcis长度【用来得到该阶段的最大长度】,那么更新nmax为maxlen[0][j]所记录的长度;若串1第1个元素==串2第j个元素,那么长度+1,即maxlen[1][j]=nmax+1;

如果上述条件都不满足的话,那么maxlen[1][j]的值等于maxlen[0][j]的值,即保留之前所记录的长度;依此类推。

     最后maxlen[len1][j] (j from 1 to len2)则保存了len2个长度值,再比较得出最大值即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 500
int n,len1,len2,nmax;
int s1[MAXN+5],s2[MAXN+5];
int maxlen[MAXN+5][MAXN+5];

int main()
{
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&len1);
        for(int i=1;i<=len1;i++)
            scanf("%d",&s1[i]);
        scanf("%d",&len2);
        for(int i=1;i<=len2;i++)
            scanf("%d",&s2[i]);
        memset(maxlen,0,sizeof(maxlen));
        for(int i=1;i<=len1;i++)
        {
            nmax=0;
            for(int j=1;j<=len2;j++)
            {
                maxlen[i][j]=maxlen[i-1][j];
                if(s1[i]>s2[j]&&nmax<maxlen[i-1][j])
                    nmax=maxlen[i-1][j];
                if(s1[i]==s2[j])
                    maxlen[i][j]=nmax+1;
            }
        }
        nmax=0;
        for(int j=1;j<=len2;j++)
            nmax=max(nmax,maxlen[len1][j]);
        if(n>=1)
            printf("%d\n\n",nmax);
        else
            printf("%d\n",nmax);
    }
    return 0;
}


---------------------------------------------------------------------------------------------------------------

J-模拟 题目链接


题解:

        非常之简单的模拟,看懂题就可以了。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main()
{
    int m1,m2;
    int r1,r2,r3;
    char s[205];
    while(scanf("%d%d",&m1,&m2)!=EOF)
    {
        r1=r2=r3=0;
        scanf("%s",s);
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            if(s[i]=='A')
                r1=m1;
            else if(s[i]=='B')
                r2=m2;
            else if(s[i]=='C')
                m1=r3;
            else if(s[i]=='D')
                m2=r3;
            else if(s[i]=='E')
                r3=r1+r2;
            else if(s[i]=='F')
                r3=r1-r2;
        }
        printf("%d,%d\n",m1,m2);
    }
    return 0;
}


----------------------------------------------------------------------------------------------------------------

A-推公式and快速乘 题目链接


题解:

        找规律推公式;
        要求组成的数列中,先有一递增或递减序列然后紧接一递增或递减序列,那么我们把最大的数,或是最小的数放
中间,把剩下的数放两边,每个数有两种选择,故2^(n-1),可以选最大数在中间,也可以选最小数在中间,所以是2^n,
又因为整个数列递增和递减分别多算了一次,故总共有2^n-2种,再mod p;
        由于1<=n<=10^18,所以结果会很大,爆long long,一开始WA了,因为只用了快速幂,即使用了快速幂也会爆
long long ,所以用快速乘+快速幂来得到ans=2^n,最后结果res=(ans-2+p)%p;//因为res-2有可能得到负数,所以加上p。
快速乘实际上和快速幂原理相同,
<快速乘模板>:

ll multi(ll a,ll b,ll m)
{
    ll ans=0;
    while(b)
    {
        if(b&1) (ans+=a) %= m;
        (a=a*2) %= m;
        b/=2;
    }
    return ans;
}


<快速幂模板>:

ll pow_mod(ll a,ll b,ll m)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=multi(res,a,m);
        a=multi(a,a,m);
        b/=2;
    }
    return res;
}

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;

ll multi(ll a,ll b,ll m)
{
    ll ans=0;
    while(b)
    {
        if(b&1) (ans+=a) %= m;
        (a=a*2) %= m;
        b/=2;
    }
    return ans;
}

ll pow_mod(ll a,ll b,ll m)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=multi(res,a,m);
        a=multi(a,a,m);
        b/=2;
    }
    return res;
}

int main()
{
    ll n,p,ans;
    while(scanf("%I64d%I64d",&n,&p)!=EOF)
    {
        if(n==1)
            ans=1%p;
        else
        {
            ans=pow_mod(2,n,p);
            ans=(ans-2+p)%p;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}



----------------------------------------------------------------------------------------------------------------

B-矩阵and快速幂 题目链接


题解:

        矩阵快速幂;
       怎么个矩阵法呢,就是由已知fn=fn-1+fn+1推到:fn-1=fn-2+fn,得到fn=fn-2-fn-1,
于是构造矩阵: (fn-1,fn-2)*[1 -1]=(fn,fn-1),得到递推关系,
                                                  [1  0]
       先特判n=1和n=2时的结果,剩下的利用矩阵和递推关系进行快速幂即可。

<矩阵快速幂模板>:

node mult(node a,node b) //进行矩阵的计算
{
    node t;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
    {
        t.m[i][j]=0;
        for(int p=0;p<2;p++)
            t.m[i][j]=((t.m[i][j]+a.m[i][p]*b.m[p][j])%MOD+MOD)%MOD;
    }
    return t;
}

void pow_mod(long long n)
{
    original.m[0][0]=1,original.m[0][1]=-1;
    original.m[1][0]=1,original.m[1][1]=0;
    ans.m[0][0]=ans.m[1][1]=1;
    ans.m[0][1]=ans.m[1][0]=0;  //将ans初始化成2阶矩阵E
    while(n)
    {
        if(n%2==1)
            ans=mult(ans,original);
        original=mult(original,original);
        n/=2;
    }
}


#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 1000000007

struct node
{
    long long m[2][2];
}original,ans;

node mult(node a,node b)
{
    node t;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
    {
        t.m[i][j]=0;
        for(int p=0;p<2;p++)
            t.m[i][j]=((t.m[i][j]+a.m[i][p]*b.m[p][j])%MOD+MOD)%MOD;
    }
    return t;
}

void pow_mod(long long n)
{
    original.m[0][0]=1,original.m[0][1]=-1;
    original.m[1][0]=1,original.m[1][1]=0;
    ans.m[0][0]=ans.m[1][1]=1;
    ans.m[0][1]=ans.m[1][0]=0;
    while(n)
    {
        if(n%2==1)
            ans=mult(ans,original);
        original=mult(original,original);
        n/=2;
    }
}



int main()
{
    long long x,y,n,res;
    scanf("%I64d%I64d%I64d",&x,&y,&n);
    if(n==1) res=(x%MOD+MOD)%MOD;
    else if(n==2) res=(y%MOD+MOD)%MOD;
    else
    {
        pow_mod(n-2);
        res=((ans.m[0][0]*y+ans.m[0][1]*x)%MOD+MOD)%MOD;
    }
    printf("%I64d\n",res);
    return 0;
}



---------------------------------------------------------------------------------------------------------------

D-计算几何 题目链接


题解:

        遍历i从0到n-1,det(ai,ai+1)除以二,再求和即得到多边形面积。

<det模板>:

double det(point a,point b)
{
    return a.x*b.y-b.x*a.y;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

struct point
{
    int x,y;
}a[105];

double det(point a,point b)
{
    return a.x*b.y-b.x*a.y;
}

int main()
{
    int n;
    double area;
    while(scanf("%d",&n)!=EOF)
    {
        area=0;
        if(n==0)
            break;
        for(int i=0;i<n;i++)
            scanf("%d%d",&a[i].x,&a[i].y);
        a[n]=a[0];
        for(int i=0;i<n;i++)
            area+=det(a[i],a[i+1])/2.;
        printf("%.1f\n",area);
    }
    return 0;
}



----------------------------------------------------------------------------------------------------------------

C-计算几何 题目链接


题解:      

        计算几何,凸包题。但这题不用弄一个凸包出来,只需要判断:
            1.题所给坐标围成的多边形是否为凸包?
                 怎样才说明是凸包? ——只要任意连续三个点的叉乘(b-a,c-b)的正负相同,则说明方向相同,
                 只要有出现正负不同的,那么就不是凸包了;
              /*以下:   p1(x1,x2)  p2(x2,y2)  p3(x3,y3)

                              s=(x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)   

                             当s>0时,p1,p2,p3三个点呈逆时针  

                             当s<0时,p1,p2,p3三个点呈顺时针

                             当s=0时,p1,p2,p3三个点在一条直线上  */


            2.判断出是凸包之后,钉子的圆心要在凸包中:
                 同样用叉乘,原理同上,(b-a,r-a)的正负与判断是否为凸包时的叉乘正负相同时,说明圆心在凸包中;
            3.钉子的半径要大于凸包各边到圆心的距离:
                 叉乘(b-a,r-a)实际上就是边<a,r><a,b>形成的平行四边形的面积,所以用叉乘除以边<a,b>的长度,
                 即可得到凸包各边到圆心的距离。
        //WA的原因在于对精度的控制,各个小函数里都需要控制好精度,eps=1e-8

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const double eps=1e-8;
double r;
struct point
{
    double x,y;
}a[10005],peg;

point sub(point a,point b)
{
    a.x-=b.x;
    a.y-=b.y;
    return a;
}

int mul(point a,point b)
{
    if(a.x*b.y-a.y*b.x>=0)
        return 1;
    else
        return -1;
}

double dis(point a,point b)
{
    if(fabs(a.x-b.x)<eps)
        return fabs(peg.x-a.x) ;
    else if(fabs(a.y-b.y)<eps)
        return fabs(peg.y-a.y) ;
    else
    {
        point c,d;
        c=sub(a,b),d=sub(peg,b);
        return fabs(c.x*d.y-d.x*c.y)/sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
}

int main()
{
    int n,i,flag;
    while(scanf("%d",&n)&&n>=3)
    {
        scanf("%lf %lf %lf",&r,&peg.x,&peg.y);
        for(int j=0;j<n;j++)
            scanf("%lf %lf",&a[j].x,&a[j].y);
        a[n]=a[0];
        flag=mul(sub(a[1],a[0]),sub(a[2],a[1]));
        for(i=1;i<n;i++)
        {
            if(flag!=mul(sub(a[i],a[i-1]),sub(a[i+1],a[i])))
                break;
        }
        if(i<n)
        {
            printf("HOLE IS ILL-FORMED\n");
            continue;
        }
        for(i=0;i<n;i++)
        {
            if(flag!=mul(sub(a[i+1],a[i]),sub(peg,a[i])))
                break;
            if(dis(a[i],a[i+1])-r<0)
                break;
        }
        if(i<n)
            printf("PEG WILL NOT FIT\n");
        else
            printf("PEG WILL FIT\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值