• 8.9 纪中集训 Day9


    T1粉刷匠

    Description

    windy有 N 条木板需要被粉刷。
    每条木板被分为 M 个格子。
    每个格子要被刷成红色或蓝色。
    windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。
    每个格子最多只能被粉刷一次。
    如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
    一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

    Input

    第一行包含三个整数,N M T。
    接下来有N行,每行一个长度为M的字符串,'0'表示红色,'1'表示蓝色。

    Output

    输出一个整数,表示最多能正确粉刷的格子数。

    Sample Input

    3 6 3
    111111
    000000
    001100

    Sample Output

    16

    Hint

    100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。

    考场思路/正解 

    一眼看去感觉就是DP,于是就开始疯狂地推状态,等推出来后已经过了一个半小时了(太弱,太弱)。

    细读题目,由于分为n块木板,所以我们可以将每块木板分开处理。通过博主一系列严密的推算,结合日象和方位等玄学,认为可设f[i][j],表示每一行到第i个数粉刷j次的最优解,zx[i][j]表示到第i行粉刷j次的最优解。从而便可推出方程式(具体方程式见程序)。

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int n,m,t,ans;
    int w[55][55],f[55][2525]/*f[i][j]表示每一行到第i个数粉刷j次的最优解*/,zx[55][2525]/*zx[i][j]表示到第i行粉刷j次的最优解*/;
    char ch[55][55];
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=n;i++)
            scanf("%s",ch[i]+1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                w[i][j]=w[i][j-1]+ch[i][j]-'0';
        for(int l=1;l<=n;l++)
        {
            memset(f,0,sizeof(f));
            for(int i=1;i<=m;i++)
                for(int j=1;j<=min(m,t);j++)
                    for(int k=1;k<=i;k++)
                        f[i][j]=max(f[i][j],f[k-1][j-1]+max(w[l][i]-w[l][k-1],i-(k-1)-(w[l][i]-w[l][k-1])));
            for(int i=1;i<=t;i++)
                for(int j=1;j<=min(i,m);j++)
                    zx[l][i]=max(zx[l][i],zx[l-1][i-j]+f[m][j]);
        }
        for(int i=1;i<=t;i++)
            ans=max(ans,zx[n][i]);
        printf("%d",ans);
        return 0;
    } 

    T2迷路

    Description

    windy在有向图中迷路了。
    该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
    现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
    注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

    Input

    第一行包含两个整数,N T。
    接下来有 N 行,每行一个长度为 N 的字符串。
    第i行第j列为'0'表示从节点i到节点j没有边。
    为'1'到'9'表示从节点i到节点j需要耗费的时间。

    Output

    输出一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

    Sample Input

    2 2
    11
    00

    Sample Output

    1

    Hint

    100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。

    考场思路

    毒啊,虽然考试时候没有打,但是认为应该是用SPFA等最短路算法然后再一堆剪枝,结果考试出来看了正解发现与此没有太大关系。

    正解

    用矩阵乘法暴力三重for去算方案数,可是如何处理边权不等的问题呢?观察数据范围,边权的取值只为0~9,点数n<=10,所以我们可以暴力拆点,将其拆成边权为一的链,然后题目就解决了。不过由于第一次接触矩阵乘法求方案数所以改的有点久(菜啊)。

    Code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define Mod 2009
    using namespace std;
    
    int n,t;
    char ch[202];
    struct Pike
    {
        int Map[202][202];
    }a,ans;
    
    Pike operator * (const Pike a,const Pike b)
    {
        Pike c;
        memset(c.Map,0,sizeof(c.Map));
        for(int i=1;i<=n*9;i++)
            for(int j=1;j<=n*9;j++)
                for(int k=1;k<=n*9;k++)
                    c.Map[i][j]=(c.Map[i][j]+a.Map[i][k]*b.Map[k][j]%Mod)%Mod;
        return c;
    }
    
    void Quick(Pike s,int t)
    {
        while(t)
        {
            if(t&1)
                ans=ans*s;
            s=s*s;
            t>>=1;
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&t);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=8;j++)
                a.Map[(i-1)*9+j][(i-1)*9+j+1]=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ch+1);
            for(int j=1;j<=n;j++)
                if(ch[j]>'0')
                    a.Map[(i-1)*9+ch[j]-(int)'0'][9*(j-1)+1]=1;
        }
        for(int i=1;i<=n*9;i++)
            ans.Map[i][i]=1;
        Quick(a,t);
        printf("%d",ans.Map[1][(n-1)*9+1]);
        return 0;
    }

    T3游戏

    Description

    windy学会了一种游戏。 对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。 最开始windy把数字按顺序1,2,3,……,N写一排在纸上。 然后再在这一排下面写上它们对应的数字。 然后又在新的一排下面写上它们对应的数字。 如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下


    1 2 3 4 5 6


    2 3 1 5 4 6


    3 1 2 4 5 6


    12 3 5 4 6


    2 3 1 4 5 6


    3 1 2 5 4 6


    1 2 3 4 5 6


    这时,我们就有若干排1到N的排列,上例中有7排。 现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。

    Input

    一个整数,N。

    Output

    一个整数,可能的排数。

    Sample Input

    3

    Sample Output

    3

    Hint

    100%的数据,满足 1 <= N <= 1000 。

    考场思路

    没有去想,所以没有思路(理直气壮)。

    正解

    正解我认为这是玄学,题解写的十分清楚我就不乱扯了。

    题解:

    Code

    #include<cstdio>
    #include<algorithm>
    #define LL long long
    using namespace std;
    
    LL n,sl,ans;
    LL book[1010],zs[1010];
    LL f[1010];
    
    void Make_zs()
    {
        for(LL i=2;i<=n;i++)
        {
            if(!book[i])
                zs[++sl]=i;
            for(LL j=i+i;j<=n;j+=i)
                book[j]=1;
        }
    } 
    
    int main()
    {
        scanf("%lld",&n);
        Make_zs();
        f[0]=1;
        for(LL i=1;i<=sl;i++)
            for(LL j=n;j>=zs[i];j--)
                for(LL k=zs[i];k<=j;k*=zs[i])
                    f[j]+=f[j-k];
        for(LL i=0;i<=n;i++)
            ans+=f[i];
        printf("%lld",ans);
        return 0;
    }

    T4windy数

    Description

    windy定义了一种windy数。
    不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
    windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

    Input

    两个整数,A B。

    Output

    一个整数,表示A~B中有多少个windy数。

    Sample Input

    1 10

    Sample Output

    9

    Hint

    100%的数据,满足 1 <= A <= B <= 2000000000 。
     

    考场思路/正解

    听别人说是数位DP,可我不会啊!于是我开始瞎搞,结果就错了。

    考后改题,发现这还真是数位DP的模板题啊。

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    using namespace std;
    
    LL a,b,suma,sumb;
    LL wz[11];
    LL f[11][11];  //f[i][j]表示长度位i且最高位为j的windy数 
    
    LL Work(LL x)
    {
        LL sum=0,g=x,sl=0;
        while(g/10!=0)
        {
            wz[++sl]=g%10;
            g/=10;
        }
        wz[sl+1]=g;
        for(int i=1;i<=sl;i++)
            for(int j=1;j<=9;j++)
                sum+=f[i][j];
        sl++;
        for(int i=1;i<g;i++)
            sum+=f[sl][i];
        for(int i=sl-1;i>=1;i--)
        {
            for(int j=0;j<wz[i];j++)
            {
                if(abs(wz[i+1]-j)>=2)
                    sum+=f[i][j];
            }
            if(abs(wz[i+1]-wz[i])<2)
                break;
        }
        return sum;
    }
    
    int main()
    {
        scanf("%lld%lld",&a,&b);
        for(int i=0;i<=9;i++)
            f[1][i]=1;
        for(int i=2;i<=10;i++)
            for(int j=0;j<=9;j++)
                for(int k=0;k<=9;k++)
                    if(abs(k-j)>=2)
                        f[i][j]+=f[i-1][k];
        printf("%lld",Work(b+1)-Work(a));
        return 0;
    }

    总结

    今天考试,三题DP,一题矩阵乘法,其中有三题是省选难度的,你要我命啊!!

    觉得自己打码速度不够快,考试时智商为0。

    距 NOIp2019 还剩 91 天      祭

     
  • 相关阅读:
    solr部署长命版后继
    reiserfs相关
    sqlite in python
    查看文件系统
    https://wiki.fourkitchens.com/dashboard.action这个技术wiki不错
    gvim菜单显示问题
    linux tips
    solr部署一气呵成版,让你多活两天
    挺好玩的C语句
    hardy ubuntu source list
  • 原文地址:https://www.cnblogs.com/Thm-V/p/11329784.html
Copyright © 2020-2023  润新知