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\n");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\n",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;
}

 

posted @ 2020-04-27 20:43  sumoier  阅读(211)  评论(0编辑  收藏  举报