• noi online round 1(入门组)


    发现还是不简单。。。想要得满分的话还是很困难的 。。。但是看了一下第一题不是很难,第二题通过动态规划,还是能得到80分,如果没有分析好的话搜索好像也能得30分还是40分。。第三题做不来就只考虑k=0的情况,也有20分,所以运气好的话能得到200分,不好100分吧~

    但是如果真让我来做,,,,算了吧,还是太菜了。。o(╥﹏╥)o


    第一题:文具订购

    这个题目真的还是需要自己在纸上去推算,就把小于14的情况能怎么去买计算出来,有特殊的情况就是不能买,如果总的钱没有14大,就直接不行;但是大于14的话,就拆掉一套来补上,比如能买3套,买完后剩1块钱,有要求全部花完,所以就拆掉一套,现在是2套剩15元,15就自己去推怎么买最合适,就直接计算出来了。就这样把剩余的钱怎样买的情况算出来就可以了

    不过实在没有什么思路的话,其实你就算那几种特殊的情况也能得很多分了~

    #include <bits/stdc++.h>
    using namespace std;
    int n; //骗分很好骗 
    int rb[14]={0,0,1,0,1,1,0,1,2,0,1,2,0,1};
    int rc[14]={0,5,4,1,0,5,2,1,0,3,2,1,4,3}; 
    int a,b,c;
    int main()
    {
        cin>>n;
        if(n==1||n==2||n==5){
            printf("-1
    ");return 0; 
        }
        int cnt=n/14;
        n=n%14;
        if(n==1||n==2||n==5)  cnt--;
        a=cnt;b=cnt+rb[n];c=cnt+rc[n];
        printf("%d %d %d
    ",a,b,c);
        return 0;
    }

    第二题:跑步

    做法有点多,但是看了一下,可能能得全分的就只有两种做法;

    第一种:用搜索的话分是得得最少的,如果去用动态规划的话分肯定是得不全,但是加上空间优化也能得80分应该,所以在这里就有一个很巧妙的做法:直接看代码吧

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005,inf=0x3f3f3f;//应该可以得80分 
    #define LL long long int
    /*因为反正就是求n的组合有多少
    所以除开五边形数定理那个做法以外,还有一种方法应该会好理解一点
    就是同样是用DP,类似于完全背包问题,
    就先求出小于根号n的情况有多少
    大于根号n的情况有多少
    然后再根据乘法原理来进行合并
    设f(n,k)为n拆成k个正整数部分有f(n,k)种方案(k由小到大排列)
    f(n,k)=f(n-1,k-1)+f(n-k,k)
    */
    int ans=0,d[N],f[N][330];
    int n,m,p;
    int main(){
        cin>>n>>p;
        m=sqrt(n)+1;
        d[0]=f[0][0]=1;
        for(int i=1;i<=m;i++)
            for(int j=i;j<=n;j++)
                d[j]=(d[j]+d[j-i])%p;//小于根n 
        m++;
        for(int i=m;i<=n;i++){
            f[i][1]=1;
            for(int j=2;j<=m;j++)
                f[i][j]=(f[i-j][j]+f[i-m][j-1])%p;
        }
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++)
                 ans=(ans+1ll*d[i]*f[n-i][j])%p;
        } 
        printf("%d",ans);
    }

    第二种:就是五边形数定理了,这个没看过的肯定想不出来,。。。好吧像我太菜了也是似懂非懂,下来去看了一下母函数的相关知识,还有就是五边形数定理,就当成一个定理记住吧,反正就记住那个值是怎样在变化的就行了。。(个人观点)

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100010,inf=0x3f3f3f;
    int n,p,f[N],g[N];
    int main()
    {
        cin>>n>>p;
        int m=0;
        for(int k=1;g[m]<=n;k++){
            g[++m]=(3*k*k-k)/2;
            g[++m]=(3*k*k+k)/2;
        }
        f[0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1,t=1;g[j]<=i;j++)
            {
                f[i]=(f[i]+f[i-g[j]]*t)%p;
                if(j%2==0) t*=-1;
            }
        cout<<(f[n]+p)%p<<endl;
                
        return 0;
    }
    /*五边形数定理:(1-x)(1-x^2)(1-x^3)...=1-x-x^2+x^5+x^7-x^12-x^15+x^22+x^26...
    指数变化规律 (3k^2-k)/2   (3k^2+k)/2 
    pn=pn-1+pn-2-pn-5-pn-7+....
    构造函数: 
    f(x)=1+p1x^1+p2x^2+....+pnx^n+.....=(1+x+x^2+x^3+..)(1+x^2+x^4+..)(1+x^3+x^6...) 
    =1/(1-x)(1-x^2)(1-x^3)...
    f(x)*(1-x)(1-x^2)...=1
    f(x)*(1-x-x^2+x^5+x^7-x^12-x^15...)=1;
    (1+p1x^1+p2x^2)*(1-x-x^2+x^5+x^7-x^12-x^15)=1;
    xn的系数:pn-pn-1-pn-2+pn-5+pn-7...=0 
    pn=...
    */

    第三题:魔法;

    好吧由于没有学过线性代数,,学习一下矩阵的乘法吧,虽然有讲过但是记不太清楚了,首先能够进行相乘的条件就是矩阵A的行数列数等于矩阵B的列数,

    例如[1 2 3] 

    [1 2 3

     1 2 3

     1 2 3 ]

    那么就等于[1*1+2*1+3*1  1*2+2*2+3*2   1*3+2*3+3*3]=[6  12   18]

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                c[i][j]+=a[i][k]*b[k][j];

    下一个知识:快速幂(相关代码)

    int quickPow(int a,int b,int n)
    {
        if(b==1) return a;
        if(b%2==0)//偶数的情况
        {
            int t=quickPow(a,b/2,n);
            return t*t%n;
        } 
        else {//奇数的情况 
            int t=quickPow(a,b/2,n);
            t=t*t%n;t=t*a%n;
            return t;
        }
    }
    int quickPow(int a,int b,int n)
    {
        int res=1;
        while(b){
            if(b%2==1) res=res*a%n;
            a=a*a%n;
            b=b/2; 
        }
        return res;
     } 
    View Code

    下一个知识:矩阵快速幂

    由矩阵乘法的定义可知,如果a*a合法,那么a的行等于a的列,所以可以快速幂的矩阵必须是方阵(行和列相等)

    矩阵:

    struct node{
        int mat[15][15];//定义矩阵
         
    }x,y; 

    矩阵相乘:

    node mul(node x,node y)//矩阵乘法
    {
        node tmp;
        for(int i=0;i<len;i++)
            for(int j=0;j<len;j++)
            {
                tmp.mat[i][j]=0;
                for(int k=0;k<len;k++)
                    tmp.mat[i][j]+=(x.max[i][k]*y.mat[k][j])%mod;
                tmp.mat[i][j]=tmp.mat[i][j]%mod;
            }            
    } 

    矩阵快速幂:

    node matpow(node x,node y,int num)//矩阵快速幂
    {
        while(num){
            if(num&1) y=mul(y,x);
            x=mul(X,x);
            num=num>>1;
        }
        return y;
    } 

    还有一种写法:

    node matpow(node x,int n)
    {
        node y;
        for(int i=0;i<len;i++) y.mat[i][i]=1;//好像是规定的对角线为1
        while(n)//后面就差不多了 
        {
            if(n&1) y=mul(y,x);
            x=mul(x,x);
            n=n>>1;
         } 
    }

    so.......思路有了,做法感觉我迷迷糊糊的写了好多,但其实都是一样的咳咳

    开始没有环的情况过不了,原因是因为mul函数里面的最大值复制与结构体中重复了,如果知道重载函数就比较好看这个代码,不知道的话就把我注释掉的结合中而看吧。。

    注释的内容结合起来是有三种不一样的写法,但其实思路都是一样的。。。代码可能有点长,因为我下面也自己写了很多

    #include <bits/stdc++.h>
    using namespace std;
    const int N=3050,M=5005,inf=0x3f3f3f3f3f3f3f;//也可以骗k=0的情况 
    long long d[105][105];
    struct edge{
        int x,y,w;
    }e[N]; 
    int n,m,K;
    struct node{
        long long mat[105][105];
        node(int x=0x3f){memset(mat,x,sizeof(mat));}
        /*
        node operator*(const node&b)const
        {
            node ans;
            for(int k=1;k<=n;k++)
                for(int i=1;i<=n;i++)
                    for(int j=1;j<=n;j++)
                        ans.mat[i][j]=min(ans.mat[i][j],mat[i][k]+b.mat[k][j]);
            return ans;
        } 
        */
    }a;
    node mul(node q,node p)
    {
        node c;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    c.mat[i][j]=min(c.mat[i][j],p.mat[i][k]+q.mat[k][j]);
        return c;
    }
    
    node matpow(node p,int q)
    {
        if(q==1) return p;
        node res=matpow(p,q>>1);
        if(q%2==1) return mul(mul(res,p),res);
        else return mul(res,res); 
    }
    /*
    node matpow(node x,int y)
    {
        node ans;
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans.mat[i][j]=d[i][j];
        while(y)
        {
            if(y&1) ans=mul(ans,x);//ans=ans*x;
            x=mul(x,x);
            y>>=1;
        }
        return ans;
    }
    */
    int main()
    {
        memset(d,0x3f,sizeof(d));
        cin>>n>>m>>K;
        for(int i=1;i<=n;i++) d[i][i]=0;
        for(int i=1;i<=m;i++){
            cin>>e[i].x>>e[i].y>>e[i].w;
            d[e[i].x][e[i].y]=e[i].w;//有向图 
        } 
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    d[i][j]=min(d[i][j],d[i][k]+d[k][j]);//Floyd求出任意两点之间的最短距离
         for(int k=1;k<=m;k++) 
         {
             int u=e[k].x,v=e[k].y,w=e[k].w;//枚举要 
             for(int i=1;i<=n;i++)
                 for(int j=1;j<=n;j++)
                     a.mat[i][j]=min(a.mat[i][j],min(d[i][j],d[i][u]+d[v][j]-w));//施展一次魔法的最佳情况(但有可能施展也有能不施展) 
        }
        if(K==0) cout<<d[1][n]<<endl;//不使用魔法的情况
        else cout<<matpow(a,K).mat[1][n]<<endl; 
    }
    
    /*预备知识 
    矩阵相乘 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                c[i][j]+=a[i][k]*b[k][j]; 
    快速幂 
    int quickPow(int a,int b,int n)
    {
        if(b==1) return a;
        if(b%2==0)//偶数的情况
        {
            int t=quickPow(a,b/2,n);
            return t*t%n;
        } 
        else {//奇数的情况 
            int t=quickPow(a,b/2,n);
            t=t*t%n;t=t*a%n;
            return t;
        }
    }
    int quickPow(int a,int b,int n)
    {
        int res=1;
        while(b){
            if(b%2==1) res=res*a%n;
            a=a*a%n;
            b=b/2; 
        }
        return res;
     } 
    矩阵快速幂 
    struct node{
        int mat[15][15];//定义矩阵
         
    }x,y; 
    node mul(node x,node y)//矩阵乘法
    {
        node tmp;
        for(int i=0;i<len;i++)
            for(int j=0;j<len;j++)
            {
                tmp.mat[i][j]=0;
                for(int k=0;k<len;k++)
                    tmp.mat[i][j]+=(x.max[i][k]*y.mat[k][j])%mod;
                tmp.mat[i][j]=tmp.mat[i][j]%mod;
            }            
    } 
    node matpow(node x,int n)
    {
        node y;
        for(int i=0;i<len;i++) y.mat[i][i]=1;//好像是规定的对角线为1
        while(n)//后面就差不多了 
        {
            if(n&1) y=mul(y,x);
            x=mul(x,x);
            n=n>>1;
         } 
    }
    node matpow(node x,node y,int num)//矩阵快速幂
    {
        while(num){
            if(num&1) y=mul(y,x);
            x=mul(X,x);
            num=num>>1;
        }
        return y;
    } 
    
    /*
    状态:f(k,i,j)表示从i到j最多用了k次魔法的所有方案的最小值
    状态转移: 
    f(k,i,j)=min(f(k,i,j),f(k-1,i,t)+f(1,t,j)); 
    有点类似矩阵乘法 
    0次:Floyd
    1次:枚举
    右边:a->b 
    所以:如果把每个 fk 看成一个矩阵,那么一次转移就是 f的一次自乘,把矩阵乘法中的加号换成 min。 
    
    */

    好了,终于把第一次的写完了~~~

    今天又写了一种:(思路更加清晰啦)

    #include <bits/stdc++.h>
    #define N 105
    #define M 3505
    #define inf 0x3f3f3f3f3f3f3f
    using namespace std;
    int u[M],v[M],t[M],n,m,kk;
    long long floyd[N][N];
    struct node{
    long long mat[N][N];
    }aa;
    node mul(node x,node y){
        node z;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) 
                z.mat[i][j]=inf;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) 
                for(int k=1;k<=n;k++) 
                    z.mat[i][j]=min(x.mat[i][k]+y.mat[k][j],z.mat[i][j]);
        return z;
    }
    node quickpow(node a,int b){
        if(b==1)return a;
        node res=quickpow(a,b>>1);
        if(b%2==1) return mul(mul(res,a),res);
        else return mul(res,res);
    }
    int main(){
        scanf("%d %d %d",&n,&m,&kk);
        memset(floyd,0x3f,sizeof(floyd));
        for(int i=1;i<=n;i++) floyd[i][i]=0;
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&u[i],&v[i],&t[i]);
            floyd[u[i]][v[i]]=t[i];
        }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++) 
                    floyd[i][j]=min(floyd[i][k]+floyd[k][j],floyd[i][j]);
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) 
                aa.mat[i][j]=inf;
        for(int k=1;k<=m;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    aa.mat[i][j]=min(min(floyd[i][u[k]]+floyd[v[k]][j]-t[k],floyd[i][j]),aa.mat[i][j]);
        if(kk==0) printf("%lld",floyd[1][n]);
        else cout<<quickpow(aa,kk).mat[1][n]<<endl;
        return 0;
    }
  • 相关阅读:
    js 去除金额的千位分隔符
    vue中的iviewUI导出1W条列表数据每次只导出2000条的逻辑
    js取整数、取余数的方法
    http协议
    vue 项目安装sass的依赖包
    浅析vue的双向数据绑定
    闭包
    Top 20 NuGet packages for captcha
    IIS URL Rewrite Module的防盗链规则设置
    IIS URL Rewrite – Installation and Use
  • 原文地址:https://www.cnblogs.com/sunny99/p/12784957.html
Copyright © 2020-2023  润新知