【USACO】2011 Feb Cowlphabet 奶牛文字

15 篇文章 0 订阅

Cowlphabet 奶牛文字


  • Description

约翰家的奶牛用别人听不懂的“牛语”联络。牛语采用英文字母,而且区分大小写。牛语中的语法中,前后字母的衔接非常重要,存在P个基本组合,每个字母之后只能接固定的几个字母。
约翰担心奶牛正在密谋反对他,于是最近一直在偷听她们的对话。可是牛语太复杂了,他只模模糊糊地听到了一个单词,并确定了这个单词中有U个大写字母,L个小写字母。约翰对这个单词很在意,他想知道,有多少牛语词汇拥有U个大写字母,L个小写字母。由于这个数字太大了,你只要输出答案取97654321的余数就可以了。

  • Input Format

第一行:三个用空格隔开的整数:U,L和P,1≤U,L≤250,1≤P≤200;
第二行到P+1行:第i+1有两个字母Ai和Bi,表示字母Ai后面可以接Bi,没有一对Ai和Bi是完全相同的。

  • Output Format

第一行:单个整数,表示符合条件的词汇数量除以97654321的余数。

  • Sample Input

2 2 7
AB
ab
BA
ba
Aa
Bb
bB

  • Sample Output

7

  • Hint

可能的单词为 AabB,ABba,abBA,BAab,BbBb,bBAa,bBbB


  • 分析

一开始还以为是道图论题,建图跑了半天。后来才发现要求方案数,那就用动规好了。
F[i][j][k] 表示已知i个字母,其中有j个大写字母,最后一个字母是k。我写的转移方程是正推的:

F[i+1][j+(E[l].to<=Z)][E[l].to]+=F[i][j][k]
处理一下初始值 F[1][1][AZ]=1,F[1][0][az]=1 ,就能过了。


#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Mod=97654321;
long long F[502][252][70],U,L,P,Ans,tot,last[70];
char s[2];
struct Data{int to,next;}E[252];
void Addline(int u,int v){E[++tot].to=v;E[tot].next=last[u];last[u]=tot;}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%lld%lld%lld\n",&U,&L,&P);
    for (int i=1;i<=P;i++){
        gets(s);
        Addline(s[0]-'A',s[1]-'A');
    }
    for (int i='A'-'A';i<='Z'-'A';i++) F[1][1][i]=1;
    for (int i='a'-'A';i<='z'-'A';i++) F[1][0][i]=1;
    for (int i=1;i<U+L;i++){
        for (int j=0;j<=U;j++){
            for (int k='A'-'A';k<='Z'-'A';k++){
                for (int l=last[k];l;l=E[l].next){
                    (F[i+1][j+(E[l].to<=('Z'-'A'))][E[l].to]+=F[i][j][k])%=Mod;
                }
            }
            for (int k='a'-'A';k<='z'-'A';k++){
                for (int l=last[k];l;l=E[l].next){
                    (F[i+1][j+(E[l].to<=('Z'-'A'))][E[l].to]+=F[i][j][k])%=Mod;
                }
            }
        }
    }
    for (int i='A'-'A';i<='Z'-'A';i++) (Ans+=F[U+L][U][i])%=Mod;
    for (int i='a'-'A';i<='z'-'A';i++) (Ans+=F[U+L][U][i])%=Mod;
    printf("%lld",Ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值