sdkd2019.3.20训练题目

A. HDU 2046
第一次看到这个,题目,想到的是动态规划,真是思维僵化了,总想套,而如果仔细想它的构成方法,就会很容易想到汉诺塔的那个想法,面对复杂的问题,先思考距离胜利一步之遥的时候要怎么解决问题。n层的状态数=n-1层状态数+n-2层状态数+1-1。如果有2层,有2种方法把它填满,竖的和横的,但是其中竖的会和补一层的相互重复,因为补1层只能用竖的。
最开始的时候发现自己思维僵化后,很自然的想到了这个,却wa了,唉,觉得是自己想错了,就又换了好多种,wa绝望,后来发现是这是一个feibonaqie数列,数会很大,应该用long long int,可惜想到这个的时候也是在换其他方法来算的,后来看了答案,才知道,自己是错在了这里。

#include<cstdio>
#include<cstring>
using namespace std;
int main(void)
{long long int all[60];
    memset(all,0,sizeof(all));

    all[1]=1;
    all[0]=1;
    for(int i=2;i<=50;i++)
        all[i]+=all[i-1]+all[i-2];
        int n;
        while(scanf("%d",&n)!=EOF)
        {
            printf("%lld\n",all[n]);
        }
    return 0;
}

B.HDU 5247
这题我觉得就是考验心态,敢不敢写,很容易想到的思路是,每次就选几个数,用sort排序,然后再检查是否符合,这自然会tle,但这题思路就是这样子,如果敢想,肯定可以找出简化的方法,比如说排序上,每次重新sort这肯定多浪费时间,因此可以想到打表,在排序上,如果用sort,那么它要重排,而实际上,只要把一个数放到一个位置上就行,可以自己写一个sort。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e4+10;
long long int all[maxn];
int ans[1009],fin[1009];
long long int  wait[1009];
bool check(int a)
{
    for(int i=1;i<a;i++)if((wait[i+1]-wait[i])!=1){return 0;}
    return 1;
}
void zisort(int to)
{
    for(int i=to;i>1;i--)
    if(wait[i]<wait[i-1])swap(wait[i],wait[i-1]);
    else return;

}
int main(void)
{int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&all[i]);
//确定表的大小
    int big=0;
    for(int i=1;i<=m;i++){scanf("%lld",&fin[i]);big=max(big,fin[i]);}
//打表
    for(int i=1;i<n;i++)
    {wait[1]=all[i];
        for(int d=2;d<=big;d++)
    {if((i+d-1)>n)break;

    wait[d]=all[i+d-1];
    zisort(d);
    if(check(d))ans[d]++;

    }
    }
    ans[1]=n;
    
    //输出
    printf("Case #1:\n");
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[fin[i]]);
        return 0;
}

D. POJ 2299——离散化和树状数组
这题拿到的时候真的想到的都是tle的做法,首先在排序上要快排一次,其次每次找要走一遍,双重循环。后来看题解,真是自己缺的知识。
先想排数组,相邻交换,就是对于第n个,发现前面有a个小于它的,而现在它新添在的位置是第n个,所以他要交换 n-a-1次,如此就得到前n个排序好的数组了,每次都如此。
对于要查找a数组前n个的和 且要实时更新 用树状数组。
https://blog.csdn.net/Small_Orange_glory/article/details/81290634 树状数组的学习链接
总结起来就是求和就 while(a>0){cnt+=p[a];a-=lowbit(a);},
更新就是while(a<=n){p[a]+=1;a+=lowbit(a);} lowbit(a) {return a&(-a);}

而用树状数组,既然是开数组,那就不能太大,但是这个题的数据范围大,所以要想到用离散化的方法,即把数组的大小排序号变为值,但是在这个地方又有一个技巧,用结构体,根据值排好虚后,结构体的成员又保留了原来的序列号,这时再把值变为排好后的序列号。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5e5+10;
int n;
struct re
{
    int val,id;
    bool operator<(const re &a)const {return val!=a.val? val<a.val:id<a.id;}
}all[maxn];
int now[maxn];
int num[maxn];
inline int lowbit(int a)
{
    return a&(-1*a);
}
int miele(int a)
{
    int cnt=0;
    while(a>0)
    {cnt+=num[a];
      a-=lowbit(a);

    }
    return cnt;
}
void add(int a)
{
    while(a<=n)
    {
        num[a]++;
        a+=lowbit(a);
    }
}
int main(void)
{
    while(scanf("%d",&n)&&n)
    {
        for(int i=1;i<=n;i++){scanf("%d",&all[i].val);all[i].id=i;}
        sort(all+1,all+1+n);
        for(int i=1;i<=n;i++)now[all[i].id]=i;
        long long int ans=0;
        memset(num,0,sizeof(num));
        for(int i=1;i<=n;i++)
        {
            int f=i-miele(now[i])-1;
            ans+=f;
            //printf("f=%d\n",f);
            add(now[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

E.HDU 1175 -------dfs
题目没看清楚明白,它是有要求的,不能转超过2次,每次dfs的时候要注意,由于这条路径走不成功可能是因为zuan太多次,但是可能有另一条路径可以走通,但是要走过这个点,因此但失败return后要记vi=0;此外zuan的时候也要注意

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1009;
int step[5][2]={{0,0},{-1,0},{1,0},{0,-1},{0,1}};
int n,m;
bool suc;
int mp[maxn][maxn];
bool vi[maxn][maxn];
void dfs(int ax,int ay,int bx,int by,int last,int zuan)
{if(zuan>2)return;
    for(int i=1;i<=4;i++)
{
    int nx=ax+step[i][0];
    int ny=ay+step[i][1];
    int p=0;
     if((i-1)/2!=(last-1)/2)p=1;
    if(last==6)p=0;
    if(nx==bx&&ny==by&&(zuan+p)<=2){suc=1;return;}
    if(mp[nx][ny]!=0||vi[nx][ny])continue;
    if(nx<1||ny<1||nx>n||ny>m)continue;
    vi[nx][ny]=1;

    dfs(nx,ny,bx,by,i,zuan+p);
    vi[nx][ny]=0;
    if(suc)return;
}

}
int main(void)
{
    while(scanf("%d %d",&n,&m)&&n&&m)
    {
        for(int i=1;i<=n;i++)
            for(int d=1;d<=m;d++)
            scanf("%d",&mp[i][d]);
            int num;
        scanf("%d",&num);
        for(int i=1;i<=num;i++)
        {int ax,ay,bx,by;
            scanf("%d %d  %d %d",&ax,&ay,&bx,&by);
            if(mp[ax][ay]!=mp[bx][by]||mp[ax][ay]==0||mp[bx][by]==0){printf("NO\n");continue;}
            suc=0;
            memset(vi,0,sizeof(vi));
            dfs(ax,ay,bx,by,6,0);
            if(suc)printf("YES\n");else printf("NO\n");
        }
    }
    return 0;
}

F. HDU 2571
第一眼看完,直觉是动态规划裸题,直接打码,样例过了,交,wa。对于这种第一直觉套模板总让我很害怕,因为模板可能是因为太常见的原因,每次用快速地没有自习思考。仔细去想这题,动态规划的原理,是每一步都是选择最优的上一步,而每一步都是从左走过来,从上走过来,所以用动态规划从一行行向右过去,是没有错的,
但是有一个边境问题,比如说每次都是max(now[i-1][d],now[i][d-1]);但如果这个now[i][d-1]不在这个地图上,而在初始的时候把所有点都设有0,而now[i-1][d]是负数,那么就错了。
此外,now[i][d]还要有一个初始值,但是由于每个格子可能会有特殊性,不能固定选择左边的那个,或者是上边的那个就是它的初始值,而这样的特殊格子有第1格,第一行,第一列,就要特别的给初始值,其他的可以是左边或者上边。
ac代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int mp[30][2000];
int now[30][3000];
int main(void)
{int cas;
    scanf("%d",&cas);
    while(cas--)
    {int r,c;
    memset(now,0,sizeof(now));
    memset(mp,0,sizeof(mp));
        scanf("%d %d",&r,&c);
        for(int i=1;i<=r;i++)
            for(int d=1;d<=c;d++)
        {scanf("%d",&mp[i][d]);
        }
        now[1][1]=mp[1][1];
        for(int i=1;i<=r;i++)
            for(int d=1;d<=c;d++)
            {

if(i==1&&d==1)continue;
if(i==1)now[i][d]=now[i][d-1];
else if(d==1)now[i][d]=now[i-1][d];
else now[i][d]=now[i-1][d];
              for(int f=2;d/f>0;f++)
              {
                  if(d%f)continue;
                  now[i][d]=max(now[i][d],now[i][d/f]);
              }
              if(i>2)now[i][d]=max(now[i][d],now[i-1][d]);
              if(d>2)now[i][d]=max(now[i][d],now[i][d-1]);
              now[i][d]+=mp[i][d];
            }
            printf("%d\n",now[r][c]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43235540/article/details/88767321