zoukankan      html  css  js  c++  java
  • CCPC Wannafly Winter Camp Div2 部分题解

    Day 1, Div 2, Prob. B - 吃豆豆

    题目大意

    wls有一个(n)(m)列的棋盘,对于第(i)行第(j)列的格子,每过(T[i][j])秒会在上面出现一个糖果,糖果只存在一秒,下一秒就会消失。

    假如wls第(k)秒在第(i)行第(j)列的格子上,满足(T[i][j]|k),则wls会得到一个糖果。

    wls每一秒只可以上下左右移动一格或停在原地。

    请问wls从指定的(S(xs,ys)​)出发到达指定的(T(xt,yt)​),并且在路上得到至少(C​)个糖果最少需要多少时间?

    wls在(S)的初始时间是第0秒。

    [1leq n,m,T[i][j]leq 10; 1leq Cleq 1018 ]

    解题思路

    (dp[i][j][k])表示吃到至少(k)颗豆豆,并且停在((i,j))处的最少时间(“最少”其实有些不准确,这个后面再解释)。那么(dp[i][j][1])就可以这样求出来了:

    [dp[i][j][1]=dist(xs,ys,i,j)+Delta t ]

    (Delta t)主要用于对(T[i][j])取整,注意(dp[xs][ys][1])就等于(T[xs][ys])。接下来可以递推求(dp[i][j][k])了:

    [dp[i][j][k]=min{dp[i'][j'][k-1]+dis(i',j',i,j)+Delta t} ]

    同样((i',j'))((i,j))相同时也要特殊考虑。

    所以重新说明一下(dp[i][j][k])的意义。它表示吃到至少(k)颗豆豆,并且停在((i,j))的某个时间。这个时间满足:给定(k),由所有的(dp[i'][j'][k])走到((i,j)),取最小值就是至少吃(k)颗豆豆,并且停在((i,j))处的最少时间。因为由(dp[i'][j'][k-1])转移到(dp[i][j][k])时,路途上可能吃了豆豆,但这一定也会被某个(dp[i''][j''][k])存储起来。

    还是有点难理解,(dp)的题还是要多做一些才能有这样的思路。

    #include <bits/stdc++.h>
    
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const double eps=1e-5;
    const int mod=1000000007;
    const int maxn=10;
    const int maxm=1018;
    
    using namespace std;
    
    int t[maxn+5][maxn+5];
    int dp[maxn+5][maxn+5][maxm+10];
    
    int dist(int x1,int y1,int x2,int y2)
    {
        return abs(x1-x2)+abs(y1-y2);
    }
    
    int main()
    {
        int n,m,c;
        scanf("%d%d%d",&n,&m,&c);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",t[i]+j);
        int xs,ys,xt,yt;
        scanf("%d%d%d%d",&xs,&ys,&xt,&yt);
    
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
           	if(i!=xs||j!=ys)
             	dp[i][j][1]=(dist(xs,ys,i,j)+t[i][j]-1)/t[i][j]*t[i][j];
         	else
             	dp[i][j][1]=t[i][j];
        }
    
        for(int k=2;k<=c;k++)
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        for(int ii=1;ii<=n;ii++)
        for(int jj=1;jj<=m;jj++)
        {
            int temp;
            if(ii==i&&jj==j)
                temp=dp[ii][jj][k-1]+t[ii][jj];
            else
                temp=(dp[ii][jj][k-1]+dist(ii,jj,i,j)+t[i][j]-1)/t[i][j]*t[i][j];
            dp[i][j][k]=min(dp[i][j][k],temp);
        }
    
        int ans=inf;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int temp=dp[i][j][c]+dist(i,j,xt,yt);
                ans=min(ans,temp);
            }
        }
    
        printf("%d
    ",ans);
    
        return 0;
    }
    
    

    Day 4, Div 2, Prob. G - 置置置换

    题目大意

    wls有一个整数(n),他想请你算一下有多少(1...n)的排列(permutation)满足:对于所有的(i(2leq ileq n)),若(i)为奇数,则(a[i-1]<a[i]),否则(a[i-1]>a[i])。请输出答案mod 1e9+7。

    [1leq nleq 1000 ]

    解题思路

    (dp[i][j])表示使用(1...i),开头为(j)的符合条件的排列数。

    容易发现,将(dp[i][j])排列对(i+1)取补,在开头加上一个比(i+1-j)大的数(j'),再把不比(j')小的数都加一,就构造了一个新的(1...i+1)的排列。转移方程如下:

    [dp[i][j]=sumlimits _{j'=i-j+1}^{i-1} dp[i-1][j'] ]

    容易发现,给定(j),这样构造是不重不漏的:每个(dp[i][j]),都可以有一个(dp[i-1][j'])与之对应;而每个(dp[i-1][j']),都可以对指定的(j),构造出一个(dp[i][j])

    再利用一个前缀和,将复杂度降到(O(n^2))

    #include <bits/stdc++.h>
    
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const double eps=1e-5;
    const int mod=1000000007;
    const int maxn=1000;
    const int maxm=300000;
    
    using namespace std;
    
    ll dp[maxn+10][maxn+10];
    
    int main()
    {
        int n;
        scanf("%d",&n);
    
        memset(dp,0,sizeof(dp));
        dp[1][1]=1;
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                dp[i][j]=(dp[i-1][i-1]-dp[i-1][i-j])%mod;
            }
            for(int j=1;j<=i;j++)
                dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;
        }
    
        printf("%lld
    ",(dp[n][n]%mod+mod)%mod);
    
        return 0;
    }
    

    Day 5, Div 2, Prob. F - Kropki

    题目大意

    你有一个(1)(n)的排列 (p_1), (p_2), ... , (p_n),对于所有的(i(1leq ileq n-1)), 如果(p_i)(p_{i+1})中,有一个数是另一个数的两倍,那么这两个数之间标一个1,否则标0。

    给定及长度为(n-1(2leq nleq 15))的01串(str[1:n-1]),问有多少(1)(n)的排列对应该01串。

    解题思路

    状压DP。(dp[i][j][k]​)表示(1...i​)位,使用过的数字状态是(j​),末位是(k​)的排列数。

    (i​)(i+1​)之间标1时,若(k imes 2leq n​)且未被使用,则(dp[i][j][k]​)可以向(dp[i+1][j'][k imes 2]​)转移:

    [dp[i+1][j|(1<<(2k-1))][2k]+=dp[i][j][k],str[i]==1,2kleq n,j&(1<<(2k-1))==0 ]

    同理可得:

    [dp[i+1][j|(1<<(k/2-1))][k/2]+=dp[i][j][k],str[i]==1,k\%2==0,j&(1<<(k/2-1))==0 ]

    [dp[i+1][j|(1<<(tmp-1))][tmp]+=dp[i][j][k],str[i]==0,1leq tmpleq n,k\%2==1||tmp eq k/2,tmp eq 2k,j&(1<<(tmp-1))==0​ ]

    #include<bits/stdc++.h>
    
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const double eps=1e-5;
    const double pi=3.141592653589793;
    const int mod=1000000007;
    const int maxn=1000;
    const int maxm=1000;
    
    using namespace std;
    
    char str[maxn+10];
    int dp[20][40000][20];
    
    int main()
    {
       int n;
       scanf("%d",&n);
       scanf("%s",str+1);
    
       memset(dp,0,sizeof(dp));
       for(int i=1; i<=15; i++)
           dp[1][1<<(i-1)][i]=1;
    
       for(int i=1; i<=n-1; i++)
       for(int j=1;j<=(1<<n)-1;j++)
       for(int k=1;k<=n;k++)
           if(dp[i][j][k]>0)
           {
               if(str[i]=='1')
               {
                   if(k%2==0&&(j&(1<<((k/2)-1)))==0)
                       dp[i+1][j|(1<<((k/2)-1))][k/2]=(dp[i+1][j|(1<<((k/2)-1))][k/2]+dp[i][j][k])%mod;
                   if(k*2<=n&&(j&(1<<((k*2)-1)))==0)
                       dp[i+1][j|(1<<((k*2)-1))][k*2]=(dp[i+1][j|(1<<((k*2)-1))][k*2]+dp[i][j][k])%mod;
               }
               else
               {
                   for(int tmp=1;tmp<=n;tmp++)
                       if((k%2==1||tmp!=k/2)&&tmp!=k*2&&(j&(1<<((tmp)-1)))==0)
                           dp[i+1][j|(1<<((tmp)-1))][tmp]=(dp[i+1][j|(1<<((tmp)-1))][tmp]+dp[i][j][k])%mod;
               }
           }
       ll ans=0;
       for(int i=1;i<=n;i++)
           ans=(ans+1LL*dp[n][(1<<n)-1][i])%mod;
       printf("%lld
    ",ans);
    
       return 0;
    }
    
  • 相关阅读:
    HTTP网页错误代码大全带解释
    记录一下手把手教您做电商网站
    C#中的Attribute
    C#中dynamic的正确用法
    【CSP】最大的矩形
    【CSP】字符与int
    C++数组初始化
    C++中输出字符到文本文档
    C++ 中时钟函数的使用
    各种函数的头文件
  • 原文地址:https://www.cnblogs.com/acboyty/p/10334561.html
Copyright © 2011-2022 走看看