2018 08 22 总结

版权声明:八月炊火的博客如需转载请注明出处 https://blog.csdn.net/qq_34990731/article/details/82010874

因为昨天一直在改题太坑了,好不容易才改对,哭死了
好了开始补总结,今天的知识点分别是数位DP、树上二分、广搜,太刺激了,都不会。
上题目:
第1题 幸运数

【问题描述】
nyz!ysu!同学非常喜欢49,为什么呢?我也不知道。。。
nyz!ysu!同学认为49是幸运数字,同时含有49的数字也是幸运数字,比如1498就是幸运数字,但是419和94就不是,这两个数字中虽然含有4和9,但没含49。nyz!ysu!同学现在想知道1~N中有多少个幸运数字(N<10^20)。但是他很笨,只好来求助于你了。

【输入】
输入文件lucky.in
一行一个正整数N
【输出】
输出文件lucky.out
共一行,为1~N中的幸运数的个数。
<答案保证不超过2^63-1范围>

【输入样例】lucky.in
50
【输出样例】lucky.out
1

【数据说明】
20%的数据保证数据 N<=1000
除上述20%的数据外,另有30%的数据保证 N=10^k -1 (0

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 105
#define ll long long 

using namespace std;

ll n,f[N][N],exp[25],ans;

int main()
{
    exp[0]=1;
    for(ll i=1;i<=20;i++)
    {
        exp[i]=exp[i-1]*10;
    }
    //预处理20位数 

    f[2][4]=1;
    //代表当长度为2时,取49的次数为1 

    for(ll i=3;i<=20;i++)
    //枚举每一位
    //先把所有的答案预处理出来 
    {
        for(ll j=0;j<=9;j++) 
        {
            f[i][0]+=f[i-1][j];
            //当长度为i时,不取数的值 = 当长度为i-1时,取0~9的每一个数的值  的和 
        }
        for(ll j=1;j<=9;j++)
        {
            f[i][j]=f[i][0];
            //长度为i时,取j = 不取数的值  因为当前第i位已经被确定 
        }
        //处理当取到4的时候的特殊情况
        f[i][4]+=exp[i-2];
        //第i位取4的值  每100中必有一个49,先累加起来 
        f[i][4]-=f[i-1][0];
        //第i位取4的值 - 第i-1位不取数 的值 因为第i-1位的值已经确定,不能再取了 
    }

    char c[100];
    ll a[1000];
    scanf("%s",c);
    ll lenc=strlen(c);
    //读入优化 
    for(ll i=0;i<lenc;i++)
    {
        a[i]=c[i]-'0';
    }

    for(ll i=0;i<lenc;i++)
    {
        //逐位枚举 
        for(ll j=0;j<a[i];j++)
        {
            ans+=f[lenc-i][j];
            //每一位只能取0~a[j]之间的数 
        }
        if(i-1>=0 && a[i-1]==4 &&a[i]==9)
        //如果出现了49,那么后面无论取什么数都不会都结果造成影响 
        {
            ll t=1;
            for(ll j=lenc-1;j>i;j--)
            //枚举后面的每一位数 
            {
                ans+=a[j]*t;
                //直接累加,乘以a[j]是因为只能取区间内的数 
                t*=10;
            }
            ++ans;
            break; 
        }
    }
    cout<<ans;
    //输出答案
    //注意开long long 
    return 0;
}

第二题:
暴力也能得50分就是强行枚举,不过会超时(TLE是一定的,这辈子都不可能不会的),通常暴力的思想就是从根开始暴力,其实这很费时间,我们换个角度,如果从叶子节点开始暴力的话是不是会更好些,这样路径就唯一了,而且看这一题的数据都大于零,也就意味着从叶子到根这一段一定是一个不减函数,那么我们就可以二分了。
上代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int head[1000001],next[1000001],riv[1000001],ver[1000001],f[1000001],size=0,dist[1000001],vist[1000001],ans=0,k;
void check(int now)
{
    int l=now,r=now;//左右都为叶子 
    while(r)//因为跟的父亲是0所以r为0意味着已经搜完根了 
    {
        if(vist[r])//如果访问过了 
            break;
        if(dist[r]<k)//总路径和小于k 
            break;
        vist[r]=1;//打个标记 
        while(dist[r]-dist[l]<k)//如果二分出来的路径和小于k,就把l上移 
            l=f[l];
        if(dist[r]-dist[l]==k)//如果等于的话答案++ 
            ans++;
        r=f[r];//r一步一步上移确保每种可能都有 
    }
}
int read()//read函数优化读入速度,也可以用cin 
{
    char ch;
    while(ch=getchar(),ch<'0' || ch>'9');
    int num=ch-'0';
    while(ch=getchar(),ch>='0' && ch<='9')
        num=num*10+ch-'0';
    return num;
}
void add(int x,int y,int u)//边表的常规操作 
{
    size++;
    next[size]=head[x];
    head[x]=size;
    riv[size]=y;
    ver[size]=u;
}
void dfs(int x,int father)//搜一下 
{
    bool flag=1;//开个标记 
    for(int i=head[x],y;y=riv[i],i;i=next[i])
    if(y!=father)//保证单向性(题目要求) 
    {
        flag=0;//不是叶子 
        dist[y]=dist[x]+ver[i];//计算每个节点到根的权值 
        f[y]=x;//父亲打上去 
        dfs(y,x);//继续搜下一个 
    }
    if(flag)//是叶子节点 
        check(x);//二分路径 
    return ;
}
int main()
{
    int n,p,i,j,x,y,u;
    scanf("%d %d %d",&n,&p,&k);
    for(i=1;i<n;i++)//读入边 
    {
        x=read();
        y=read();
        u=read();
        add(x,y,u);//边表(或者叫邻接表)的储存 
        add(y,x,u);//要正向反向都存一遍,因为不知道数据是不是从父亲到儿子 
    }
    dfs(p,0);//我们dfs一下,处理每个节点的父亲节点,并且找到叶子 ,第一个传的是当前要搜的,第二个是它的父亲,上面边表存了
    //两遍为了保证单向性所以要传它的父亲来确认 
    cout <<ans;
    return 0;
}

第三题:
第三题就比较有趣了,这一题要广搜(也算不上把,就是用了广搜的思想),我们用f[i][j],表示(i,j)这个点的水位,a[i][j]表示(i,j)这一个点的地方高度,答案就是f数组-a数组,这样我们就可以知道,f[i][j]=min(f[i][j],max(f[x][y],a[i][j]))表示对于每个点我们枚举它周围的四个点的每一个点,对于它我们可以知道它水位高度一定是周围四个点传给它的最小的,那么怎么传呢,传的就是周围的水位嘛,还有一个就是自己地面的高度,这么理解,对于一个点水位肯定是和周围一样嘛,然后还有一种情况自身高度大于周围水位,那么就只能是自己高度了表示没水,然后有一个细节,我们得到这个转移是在周围所有水位都知道的情况下,这个怎么办呢?我们可以用贪心,我们先去计算地方高度高的点这样就可以用它去更新更多的点,因为水往低处流,这样就AC了。
代码:

#include<queue> 
#include<cstdio>
#include<iostream>
#define N 1000
using namespace std;

struct point//定义结构体 
{
    int x,y,z;
    point(int _x=0,int _y=0,int _z=0)//结构体中的函数,表示每次将x,y,z初始化为0 
    {
        x=_x;
        y=_y;
        z=_z;
    }
};

priority_queue<point>heap;//优先队列,实现我刚刚说的贪心 
bool operator < (const point &a,const point &b)//重载运算符 
{
    return a.z>b.z;
}

int read()//读入优化 
{
    char c;
    while(c=getchar(),(c<'0') || (c>'9'));
    int s=int(c)-'0';
    while(c=getchar(),('0'<=c) && (c<='9'))
    s=s*10+int(c)-48;
    return s;
}

int a[N][N];
int f[N][N];
int vist[N][N];
int dx[5]={0,0,1,0,-1};//定义广搜方向,然而第0维没什么用 
int dy[5]={0,1,0,-1,0};

int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);//读入数据 
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    {
        a[i][j]=read();
        f[i][j]=k;
    }

    int ex,ey;
    scanf("%d%d",&ex,&ey);
    f[ex][ey]=a[ex][ey];//初始化使出水口水面高度为自己的海拔高度 

    heap.push(point(ex,ey,a[ex][ey]));//优先队列了解一下 
    while(!heap.empty())//广搜 
    {
        point u=heap.top();
        heap.pop();
        int x1=u.x;
        int y1=u.y;
        vist[x1][y1]=0;//打个标记 
        for(int i=1;i<=4;++i)//开始搜 
        {
            int x2=x1+dx[i];
            int y2=y1+dy[i];
            if(1<=x2&&1<=y2&&x2<=n&&y2<=m)//没有超出去 
            {
                int temp=max(f[x1][y1],a[x2][y2]);//算出广搜到的点可以传给自身的值 
                if(f[x2][y2]>temp)//如果小于自身更新 
                {
                    f[x2][y2]=temp;
                    if(!vist[x2][y2])
                    {
                        vist[x2][y2]=1;
                        heap.push(point(x2,y2,f[x2][y2]));
                    }
                }
            }
        }
    }

    for(int i=1;i<=n;++i,cout<<"\n")//输出答案 
    for(int j=1;j<=m;++j)
    printf("%d ",f[i][j]-a[i][j]);
    fclose(stdin);
    fclose(stdout);
}

终于写完了这篇总结,还有三篇加油,你们也是。

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/82010874
今日推荐