【考试记录】20180927

T1(Loj2154):

一共两行的扫雷游戏,第一行没雷,第二行没数,现在给出第一行的N个数,问第二行的雷有多少种可能的摆放方式。N<=10^4。

 

题解:

由于每一个格子有没有雷只会与它正上方的三个格子中的数有关,每个数最多只有3,可以考虑一遍平推式dp求出答案。

设dp[i][0/1][0/1][0/1]表示处理到第i个格子,该格子前三个有或者没有雷,每次判断第i-1个格子是否合法并转移。

考场上考虑到这就可以写了,100pts。

但其实精通扫雷的同学会发现一个厉害的性质:如果只有一行雷并且知道上一行的数是什么,

那么只要确定了前两个格子有没有雷,就可以通过每个格子的数推出后面所有格子是否有雷。

换句话说,只要枚举前两个格子有没有雷,后面所有格子就要么无解,要么解唯一。

 

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 100005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long

int A[MAXN],B[MAXN];
int num[4][2]={{0,0},{1,0},{0,1},{1,1}};
inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

int main(){
    int N=read(),ans=0;
    for(int i=1;i<=N;i++) A[i]=read();
    for(int k=0;k<4;k++){
        bool flag=0;
        int n1=num[k][0],n2=num[k][1];
        B[1]=n1,B[2]=n2;
        if(n1+n2!=A[1]) continue;
        for(int i=2;i<=N;i++)
            B[i+1]=A[i]-B[i-1]-B[i];
        for(int i=1;i<=N;i++)
            if(B[i-1]+B[i]+B[i+1]!=A[i] || B[i]<0 || B[i-1]<0 || B[i+1]<0 || B[i]>1 || B[i-1]>1 || B[i+1]>1)
                {flag=1;break;}
        if(B[0]!=0 || B[N+1]!=0) flag=1;
        if(!flag) ans++;
    }
    printf("%d\n",ans);
    return 0;
}

 

T2(Loj2424):

有两个仅包含小写字母的字符串A和B,现在要从字符串A中取出k个互不重叠的非空子串,然后把这k个子串按照其在字符串A中出现的顺序依次连接起来得到一个新的字符串,请问有多少方案可以使得这个新串与B相等?

|A|<=1000,|B|<=200,k<=|B|。

 

题解:

一般来说类似于两个串取子串的问题,dp是一种解法。

设dp[i][j][k][0/1]表示A取到i,B匹配到j,取了k个串,A[i]这个字符不取/取的方案数。

  • 若取这个字符,则需要A[i]==B[j],取的时候可以继承上一个串或者新开一个串。那么得到dp[i][j][k][1]=dp[i-1][j-1][k][1]+dp[i-1][j-1][k-1][0/1]。
  • 若不取这个字符,那么当前字符对答案没有影响,得到dp[i][j][k][0]=dp[i-1][j][k][0/1]。

我们发现最后一维[0/1]这一种状态多次出现,[0]在转移时压根没出现,那么可以将[0]改成取不取均可的方案数进行转移。

但这样dp内存会过大,注意到dp[i]只与dp[i-1]有关,那么我们可以把第一维压掉。

NOIP2015D2T2就做完了,100pts。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 1005
#define MAXM 205
#define INF 0x7fffffff
#define ll long long
#define mod 1000000007

char A[MAXN],B[MAXM];
ll dp[MAXM][MAXM][2];
ll tp[MAXM][MAXM][2];
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

int main(){
    ll N=read(),M=read(),K=read();
    cin>>A+1>>B+1;
    dp[0][0][0]=1;
    for(ll i=1;i<=N;i++){
        memcpy(tp,dp,sizeof(dp));
        for(ll j=1;j<=M;j++)
            for(ll k=1;k<=K;k++){
                if(A[i]==B[j]) tp[j][k][1]=(dp[j-1][k][1]+dp[j-1][k-1][0])%mod;
                else tp[j][k][1]=0;
                tp[j][k][0]=(tp[j][k][1]+dp[j][k][0])%mod;
                //cout<<i<<" "<<j<<" "<<k<<" "<<tp[j][k][0]<<endl;
            }
        memcpy(dp,tp,sizeof(tp));
    }
    printf("%lld\n",dp[M][K][0]%mod);
    return 0;
}

T3(Loj6185):

求N个点组成的每个点度数不超过4且根节点度数不超过3的有根树的个数。

 

题解:

从“每个点度数不超过4且根节点度数不超过3”这句话我们就可以发现处理完大小为n的树后往上连一条边变为某棵树的子树依然是满足条件的。这给了我们dp转移的提示。

设dp[n]表示有多少棵大小为n的树满足要求,由于根节点最多有三棵子树可以直接枚举三棵子树的大小i,j,k(人为规定顺序i<=j<=k)。

然后我在考场上开心的写出了dp[n]+=dp[i]*dp[j]*dp[k]这个转移方程。拿到了0pts。

因为子树是无序的,那么如果有两棵子树相等,dp[i]*dp[j]就必定会出现重复状态(i中第一个状态+j中第二个状态和i中第二个状态+j中第一个状态被认为是同样的)。

所以我们需要分类讨论子树大小是否会出现相等的情况。

  • 如果i==j==k,三棵子树大小全部相等,那么相当于从dp[i]中任取三个状态,可以重复取的方案数。

    此时设第i种状态取了xi个,有∑xi=3。相当于在3个物品中插入dp[i]-1个板使其分成dp[i]份,每份可以为空。

    容易得到dp[n]+=C(dp[i]+3-1,dp[i]-1)=C(dp[i]+3-1,3)。

    (这也是可重复组合数的模型,即从{a}的n个元素中取出r个元素,可以重复取的方案数=C(n+r-1,n-1))。

  • 如果i==j!=k,相当于从dp[i]中任取两个状态的方案数*dp[k]。dp[n]+=C(dp[i]+2-1,2)*dp[k]。
  • 如果i!=j==k,相当于从dp[j]中任取两个状态的方案数*dp[i]。dp[n]+=C(dp[j]+2-1,2)*dp[i]。
  • 如果i!=j!=k,所有状态都可以随意组合,dp[n]+=dp[i]*dp[j]*dp[k]。

 

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 100005
#define MAXM 15
#define INF 0x7fffffff
#define mod 1000000007
#define ll long long

ll dp[MAXN],inv[MAXN];
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

inline ll C(ll x,ll y){
    ll ans=1,ans1=1;
    for(ll i=y;i>=1;i--) ans1*=i;
    for(ll i=x;i>=x-y+1;i--) ans*=i%mod,ans%=mod;
    return ans*inv[ans1]%mod;    
}

int main(){
    ll N=read();inv[0]=0;inv[1]=1;dp[0]=1;
    for(ll i=2;i<=MAXM;i++) inv[i]=(-inv[mod%i]*(mod/i)%mod+mod)%mod;
    for(ll n=1;n<=N;n++)
        for(ll i=0;i<=N;i++)
            for(ll j=i;j<=N;j++){
                ll k=n-1-i-j;
                if(k<j || k<i) break;
                if(i==k) dp[n]+=C(dp[i]+3-1,3)%mod,dp[n]%=mod;
                else if(i==j) dp[n]+=C(dp[i]+2-1,2)%mod*dp[k]%mod,dp[n]%=mod;
                else if(j==k) dp[n]+=C(dp[j]+2-1,2)%mod*dp[i]%mod,dp[n]%=mod;
                else dp[n]+=dp[i]%mod*dp[j]%mod*dp[k]%mod,dp[n]%=mod;
            }        
    printf("%lld\n",dp[N]);
    return 0;
}
/*
*/

猜你喜欢

转载自www.cnblogs.com/YSFAC/p/9746222.html