▶ 一个学生的考勤状况是一个字符串,其中各字符的含义是:A 缺勤,L 迟到,P 正常。如果一个学生考勤状况中 A 不超过一个,且没有连续两个 L(L 可以有多个,但是不能连续),则称该学生达标(原文表述:A student could be rewarded if his attendance record doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late). )

▶ 551. 给定一个学生的考勤状况,判断他能否达标。

● 代码,4 ms,简单的计数,最快的解法算法与之相同

 class Solution
{
public:
bool checkRecord(string s)
{
int i, a, l;
for (i = a = l = ; i < s.size(); i++)
{
if (s[i] == 'A')
a++;
if (s[i] == 'L')
l++;
else // 出现 A 或 P 时均要清空 L 计数
l = ;
if (a >= || l > )
return false;
}
return true;
}
};

▶ 552. 给定正整数 n,考虑所有长度为 n 的考勤状况(3n 种可能),计算达标的考勤状况数,该数字可能非常大,取关于 109 + 7(竟然是个素数)模作为输出结果

● 自己的代码,29 ms,时间复杂度 O(n),考虑递推数列,一个达标序列只能是以下三种情况:

a[ k ] 表长度为 k,以 P 结尾,不含 A 的达标序列个数

b[ k ] 表长度为 k,以 PL 结尾,不含 A 的达标序列个数(不能是 LLLP 结尾)

c[ k ] 表长度为 k,以 LL 结尾,不含 A 的达标序列个数(不能是 LLL 结尾)

注意到 a[ 1 ] = b[ 1 ] = 1,c[ 1 ] = 0,a[ k ] = a[ k - 1 ] + b[ k - 1 ] + c[ k - 1](任意一种达标序列添个 P),b[ k ] = a[ k - 1 ](P 结尾的达标序列添个 L),c[ k ] = b[ k - 1 ](PL 结尾的达标序列添个 L)

约化后就变成了 a[ 1 ] = 1,a[ 2 ] = 2,a[ 3 ] = 4,a[ n ] = a[ n - 1 ] + a[ n - 2 ] + a[ n - 3 ]  ( n ≥ 4 )

对上述数列打表,使用长度为 n+1 的表 f,其中 a[ k ] = f[ k - 1 ],即所有下标有一单位的平移,在代码中注意控制

因为打表序列中 A 至多一个,所以按照 A 的位置分类讨论:

① 不含A,一共有 a[ n ] + b[ n ] + c[ n ] = a[ n + 1 ] 种情况,等于 f[ n ](打表要长一格的原因)

② A 在开头,一共有 a[ n - 1 ] + b[ n - 1 ] + c[ n - 1 ] = a[ n ] 种情况,等于 f[ n - 1 ]

③ A 在结尾,同上,等于 f[ n - 1 ]

④ A 在中间,考虑 A 的左边有长度为 i 的不含 A 的序列,且 A 的右边有长度为 n - i - 1 的不含A的序列,一共是 ( a[ i ] + b[ i ] + c[ i ] ) * ( a[ n - i - 1 ] + b[ n - i - 1 ] + c[ n - i - 1 ] ),即 f[ i ] * f[ n - i - 1 ],
i 在 i = 1 到 i = n - 2 之间求和。

注意每一步取模运算,因为 f 本身是指数级增长的。

 class Solution
{
public:
int checkRecord(int n)
{
if (n == )
return ;
const int m = ;
vector<long long> f(n + , );
int i,sum;
for (f[] = , f[] = , f[] = , i = ; i <= n; f[i] = (f[i - ] + f[i - ] + f[i - ]) % m, i++);
for (sum = (f[n] + f[n - ] * ) % m, i = ; i < n - ; i++)
sum = (sum + f[i] * f[n - i - ]) % m;
return sum;
}
};

● 代码,4 ms,使用一个矩阵的幂来计算递推

 #define ENTRY(A, i, j)  ((A)[(i) * n + (j)])

 int64_t* NewMatrix(size_t n)
{
assert(n >= );
int64_t* temp = (int64_t*)calloc(n * n, sizeof(int64_t));
assert(temp != NULL);
return temp;
} void FreeMatrix(int64_t* A)
{
free(A);
} void SetMatrix(int64_t* A, int64_t* B, size_t n)
{
memcpy(A, B, n * n * sizeof(int64_t));
} void IdentityMatrix(int64_t* A, size_t n)// 将方阵 A 赋值为单位方阵
{
int i, j;
for (i = ; i < n; i++)
{
for (j = ; j < n; j++)
ENTRY(A, i, j) = (i == j);
}
} void MatrixMultiply(int64_t* A, int64_t* B, size_t n)// 方阵乘法 A = A * B
{
assert(n >= );
int64_t* C = NewMatrix(n);
int i, j, k;
for (i = ; i < n; ++i)
{
for (j = ; j < n; ++j)
{
ENTRY(C, i, j) = ;
for (k = ; k < n; ++k)
ENTRY(C, i, j) += ENTRY(A, i, k) * ENTRY(B, k, j);
}
}
memcpy(A, C, n * n * sizeof(int64_t));
FreeMatrix(C);
} void MatrixPower(int64_t* C, int64_t* A, size_t n, int m)// 方阵幂 C = A ^ m
{
assert(n >= );
assert(m >= );
int64_t* B = NewMatrix(n);
SetMatrix(B, A, n);
IdentityMatrix(C, n);
for (; m > ; m /= )// 二进制方法
{
if (m % == )
MatrixMultiply(C, B, n);
MatrixMultiply(B, B, n);
}
FreeMatrix(B);
} void MatrixModulus(int64_t* A, size_t n, int64_t modulus)// 方阵逐格求模 A %= m
{
int i, j;
for (i = ; i < n; ++i)
{
for (j = ; j < n; ++j)
ENTRY(A, i, j) = ENTRY(A, i, j) % modulus;
}
} void MatrixModulusPower(int64_t* C, int64_t* A, size_t n, int m, int64_t modulus)// C = A ^ m % modulus
{
assert(n >= );
assert(m >= );
int64_t* B = NewMatrix(n);
SetMatrix(B, A, n);
IdentityMatrix(C, n);
for (; m > ;m/=)
{
if (m % == )
{
MatrixMultiply(C, B, n);
MatrixModulus(C, n, modulus);
}
MatrixMultiply(B, B, n);
MatrixModulus(B, n, modulus);
}
FreeMatrix(B);
} int AttendanceNumber(int m)
{
assert(m >= );
size_t n = ;
int64_t initial_vector[] = { , , , , , };
int64_t modulus = ;
int64_t* A = NewMatrix(n);
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = -;
ENTRY(A, , ) = -;
ENTRY(A, , ) = -;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
int64_t answer = ;
if (m <= )
answer = initial_vector[m];
else
{
int64_t* C = NewMatrix(n);
MatrixModulusPower(C, A, n, m - n + , modulus);
for (size_t i = ; i < n; ++i)
answer += ENTRY(C, n - , i) * initial_vector[i];
answer = (answer % modulus + modulus) % modulus;
FreeMatrix(C);
}
FreeMatrix(A);
return answer;
} #undef ENTRY class Solution
{
public:
int checkRecord(int n)
{
return AttendanceNumber(n);
}
};

● 初版动态规划(MLE),f[ i ][ j ][ k ] 表示长度为 i,至多 j 个 A,至多 k 个连续 L 的序列。所求目标为 f[ n ][ 1 ][ 2 ],转移方程如下,发现可以用矩阵幂来一次性计算 f[ n ]

  

 class Solution
{
public:
int checkRecord(int n)
{
const int MOD = ;
vector<vector<vector<int>>> f(n + , vector<vector<int>>(, vector<int>(3, 0)));
int i, j, k, val;
for (i = ; i < ; i++)
for (j = ; j < ; f[][i][j++] = );
for (i = ; i <= n; i++)
{
for (j = ; j < ; j++)
{
for (k = ; k < ; k++)
{
val = f[i - ][j][]; // P
if (j > ) // A
val = (val + f[i - ][j - ][]) % MOD;
if (k > ) // L
val = (val + f[i - ][j][k - ]) % MOD;
f[i][j][k] = val;
}
}
}
return f[n][][];
}
};

● 改进动态规划,8 ms,直接用矩阵幂来计算

 class Solution
{
public:
const int MOD = , M = ;
void mul(vector<vector<int>>& A, vector<vector<int>>& B)
{
vector<vector<int>> temp(M, vector<int>(M, ));
int i, j, k;
for (i = ; i < M; i++)
{
for (j = ; j < M; j++)
{
for (k = ; k < M; k++)
temp[i][j] = (int)((temp[i][j] + (long)A[i][k] * B[k][j]) % MOD);
}
}
A = temp;
return;
}
void pow(vector<vector<int>>& A, int n)
{
vector<vector<int>> temp(M, vector<int>(M, ));
for (int i = ; i < M; temp[i][i] = , i++);
for (; n > ; n /= )
{
if (n % )
mul(temp, A);
mul(A, A);
}
A = temp;
return;
}
int checkRecord(int n)
{
vector<vector<int>> A = \
{
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
};
pow(A, n + );
return A[][];
}
};

● 代码,深度优先遍历,TLE,说明穷举肯定不行

 class Solution
{
public:
int checkRecord(int n)
{
long long res = ;
dfs(n, , , , res);
return res % ;
}
void dfs(int n, int s, int A, int L, long long& res)// 当前共 s 位,有 A 个 A 和 L 个 L
{
if (s == n)
{
res++;
return;
}
dfs(n, s + , A, , res); //add "P"
if (A < )
dfs(n, s + , A + , , res);
if (L <= )
dfs(n, s + , A, L + , res);
}
};

551. Student Attendance Record I + Student Attendance Record II的更多相关文章

  1. 1 实现添加功能 1.1 定义一个学员类(Student),在Student类中定义姓名、性别和年龄属性,定义有 参数的构造方法来初始化所以的成员属性 1.2 创建学员类对象来存放学员信息,并且为每一个学生对象添加的相应的编号。并将 学员类对象添加到Map<Integer,Student>集合中 1.3 添加完成后,显示所有已添加的学员姓名 1.4 限制年龄文本框只能输入正整数,否则的会采

    学生类 package com.lanxi.demo1_3; public class Student { private String name; private String sex; priva ...

  2. 构建一个学生Student,根据类Student的定义,创建五个该类的对象,输出每个学生的信息,计算并输出这五个学生Java语言成绩的平均值,以及计算并输出他们Java语言成绩的最大值和最小值。

    定义一个表示学生信息的类Student,要求如下: (1)类Student的成员变量: sNO 表示学号: sName表示姓名: sSex表示性别: sAge表示年龄: sJava:表示Java课程成 ...

  3. GitHub Student Developer Pack创建个人网站

    链接:https://zhuanlan.zhihu.com/p/20531579 这个开发包里有什么?作为学生开发者,如何最大化利用它的价值? Atom编辑器,GitHub推出的编辑器,和Sublim ...

  4. C# 9.0新特性详解系列之五:记录(record)和with表达式

    1 背景与动机 传统面向对象编程的核心思想是一个对象有着唯一标识,表现为对象引用,封装着随时可变的属性状态,如果你改变了一个属性的状态,这个对象还是原来那个对象,就是对象引用没有因为状态的改变而改变, ...

  5. C#中的记录(record)

    从C#9.0开始,我们有了一个有趣的语法糖:记录(record) 为什么提供记录? 开发过程中,我们往往会创建一些简单的实体,它们仅仅拥有一些简单的属性,可能还有几个简单的方法,比如DTO等等,但是这 ...

  6. Method and apparatus for encoding data to be self-describing by storing tag records describing said data terminated by a self-referential record

    A computer-implemented method and apparatus in a computer system of processing data generated by a f ...

  7. salesforce 零基础学习(六十二)获取sObject中类型为Picklist的field values(含record type)

    本篇引用以下三个链接: http://www.tgerm.com/2012/01/recordtype-specific-picklist-values.html?m=1 https://github ...

  8. SQL Server 堆表行存储大小(Record Size)

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 堆表行记录存储格式(Heap) 案例分析(Case) 参考文献(References) 二.背 ...

  9. Student管理系统

    使用三层架构实现Student管理系统,分为Studrnt.Model层,Student.DAL层,Student.BLL层和Student.UI层 步骤分析: 1.在Student.Model添加S ...

随机推荐

  1. ACM比赛学习指导(20180223)

    0.比赛介绍 (1)浙江省大学生程序设计比赛,3人一组,题目为英文 (2)ACM程序设计比赛 百度百科介绍 1.理论学习 (1)<挑战程序设计竞赛>,秋叶拓哉,巫泽俊 (2)<算法竞 ...

  2. Lightoj-1220

    https://vjudge.net/problem/LightOJ-1220 求x=bp中最大的p,x可能为负数. 因数分解,x=p1x1*p2x2*...*pnxn x=(p1x1'*p2x2'* ...

  3. UVALive-3415 Guardian of Decency (最大独立集)

    题目大意:一个老师要带一些学生去春游,但是要带的学生中任意两个人都满足下面四个条件中的至少一个:1.性别相同:2.身高差大与40公分:3.最喜欢的音乐类型不同:4.最喜欢的体育运动相同.问老师最多能带 ...

  4. js中字符串处理成数字的方法

    <script> var a="11.1111"; var b="12.2222"; //第一种方法:乘以1的方法 //alert(a*1+b*1) ...

  5. ubuntu16.04安装Nvidia显卡驱动、CUDA8.0和cudNN V6

    Nvidia显卡驱动安装 在ubuntu搜索框输入 软件更新,打开 "软件和更新" 对话框,在 附加驱动里选择系统检测到的Nvidia驱动,应用更改,重启系统: 安装完成之后查看G ...

  6. Buildroot make网卡interfaces文件被修改

    /******************************************************************************** * Buildroot make网卡 ...

  7. Yii用AJAX注册验证

    <script type="text/javascript"> $(document).ready(function(){ $('#RegisterForm_usern ...

  8. 小数第n位

    问题描述 我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数. 如果我们把有限小数的末尾加上无限多个0,它们就有了统一的形式. 本题的任务是:在上面的约定下,求整数除法小数点后的第n位开始 ...

  9. (经典) K&R的名著<<C程序设计语言>>二分查找

    #include<stdio.h> //查找成功则返回所在下标否则返回-1 int binsearch(int A[], int n,int a) { int low, high, mid ...

  10. NET简单的一个画图程序

    using System; using System.Drawing; //HttpUtility.UrlEncode /// <summary> ///Curve 的摘要说明 /// & ...