904 Winter is coming

思路

难题。首先简化问题, \(n\) 个0与 \(m\) 个1排成一列,连续的0不能超过x个,连续的1不能超过y个,求排列方法数。

显然会想到这是动态规划。最快想到的方法是 \(dp[i][j][x][y]\) 表示已经有i个北境兵j个野人参与排列,且末尾有x个连续北境士兵或y个连续野人士兵的方案数。这方法显然是正确的,但是光是 \(dp[200][200][10][10]\) 数组已经十分接近本题内存限制了,保证MLE。状态转移方法是大模拟,四层for循环,每次增加一个人放在最后,讨论各种情况。具体代码可见MLE参考代码,比较好理解。

不过这个方法已经和答案很接近了,只需要稍微优化一下。可以发现第三维和第四维很多空闲的空间被浪费了,我们没有必要用两个维度来分别记录有几个0或1,可以把第四维变成一个标志位,0代表北境军,1代表野人军,而第三维记录最后一段连续的人数,这样空间变成原来的1/6,算是简单的优化了一下,思想并没有变。具体可见优化代码,在此感谢孟尧提供。

本题还可以继续优化,换种方式,dp[i][j][k]:已经有i个北境兵j个野人参与排列,第三维k是标志位(0代表北境军,1代表野人军)的排列方法数。状态转移方程变为:: \(dp[i][j][0]=∑(dp[i-k][j][1])%MOD\) ;其中 \(k∈[1,min(i,x)]\) 。同理, \(dp[i][j][1]=∑(dp[i][j-k][0])%MOD\) ;其中 \(k∈[1,min(j,y)]\) 。相信你很快就能看懂,这里用 \(dp[i-k][j][1]\) 来代表最后有k个0,相当于同时把三四维合并了,巧妙至极。具体可见最优参考代码。

分析

本题不卡时间,卡的是内存。目的是让大家在解决问题的时候有优化的思想(实际的目的是把它从中等题变成难题)。

DP只能意会,不可言传。大家在做DP题的时候一定要理清思路,一般是先不管空间,毕竟以空间换时间,大多数题都是先卡时间再卡空间的。

以本题为例粗略讲解一下DP,以后不会再讲。记住DP具备的两个要素:最优子结构和子问题重叠,见《算法导论》225页。本问题明显备最优子结构,最少的排列数是由多个较短一点的最少排列数组成。DP的多层循环也是有规律的,因为子问题的重叠,你得先把子问题算出来,才能计算更深层的。这里i和j从小到大地计算,保证所加上的都是已经计算过的,才不会出现问题,如果这题加一个dp[i+1][j][1],那明显不对了,因为这个还没有计算过。状态转移方程有时候很微妙,需要一番数学推理。

最优参考代码

//
// Created by AlvinZH on 2017/10/24.
// Copyright (c) AlvinZH. All rights reserved.
// #include <cstdio>
#include <cstring>
#include <iostream>
#define MOD 1000007
using namespace std; int n, m, x, y;
int dp[205][205][2]; int main()
{
while(~scanf("%d %d %d %d", &n, &m, &x, &y))
{
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= x; ++i)
dp[i][0][0] = 1;
for (int i = 0; i <= y; ++i)
dp[0][i][1] = 1; for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = 1; k <= min(i,x); ++k)
dp[i][j][0] = (dp[i][j][0] + dp[i-k][j][1]) % MOD; for (int k = 1; k <= min(j,y); ++k)
dp[i][j][1] = (dp[i][j][1] + dp[i][j-k][0]) % MOD;
}
} printf("%d\n", (dp[n][m][0] + dp[n][m][1]) % MOD);
}
} /* 把第三四维合并,因为我们只要在状态转移的时候保证最后连续一段不超过x或y就好了,第三维用来记录最后连续的是0还是1就好了。
* dp[i][j][k]:已经有i个北境兵j个野人参与排列,k为标志位(0代表北境军,1代表野人军)的排列方法数。
*/

优化参考代码

/*
Author: 孟爻(12593)
Result: AC Submission_id: 403884
Created at: Sun Nov 12 2017 23:05:10 GMT+0800 (CST)
Problem: 904 Time: 73 Memory: 11232
*/ #include <cstdio>
#include <cstring> long f[205][205][15][2];
long M = 1000007;
long n,m,x,y; int main() {
while(~scanf("%ld%ld%ld%ld",&n,&m,&x,&y))
{
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(long i=0; i<=n; i++) {
for(long j=0; j<=m; j++) {
if(i) {
for(long k=1; k<=x; k++)
f[i][j][k][0]=(f[i][j][k][0]+f[i-1][j][k-1][0])%M;
for(long k=0; k<=y; k++)
f[i][j][1][0]=(f[i][j][1][0]+f[i-1][j][k][1])%M;
}
if(j) {
for(long k=1; k<=y; k++)
f[i][j][k][1]=(f[i][j][k][1]+f[i][j-1][k-1][1])%M;
for(long k=0; k<=x; k++)
f[i][j][1][1]=(f[i][j][1][1]+f[i][j-1][k][0])%M;
}
}
}
long ans=0;
for(long k=1; k<=x; k++)
ans=(ans+f[n][m][k][0])%M;
for(long k=1; k<=y; k++)
ans=(ans+f[n][m][k][1])%M; printf("%ld\n",ans);
}
}

MLE代码

//
// Created by AlvinZH on 2017/10/24.
// Copyright (c) AlvinZH. All rights reserved.
// #include <cstdio>
#include <cstring>
#define MOD 1000007 int n, m, x, y;
int dp[205][205][12][12]; int main()
{
while(~scanf("%d %d %d %d", &n, &m, &x, &y))
{
memset(dp, 0, sizeof(dp));
dp[0][0][0][0] = 1;
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
for (int k = 0; k <= x; ++k) {
for (int l = 0; l <= y; ++l) {
if(dp[i][j][k][l] == 0) continue;
if(i != n && k != x)//末尾是北境兵
{
dp[i+1][j][k+1][0] += dp[i][j][k][l];
dp[i+1][j][k+1][0] %= MOD;
}
if(j != m && l != y)//末尾是野人兵
{
dp[i][j+1][0][l+1] += dp[i][j][k][l];
dp[i][j+1][0][l+1] %= MOD;
}
}
}
}
}
int ans = 0;
for (int i = 1; i <= x; ++i)
ans = (ans + dp[n][m][i][0]) % MOD;
for (int i = 1; i <= y; ++i)
ans = (ans + dp[n][m][0][i]) % MOD; printf("%d\n", ans);
}
} /*
* dp[i][j][x][y]表示已经有i个北境兵j个野人参与排列,且末尾有x个连续北境士兵或y个连续野人士兵的方案数。
*/

2016级算法第三次上机-G.Winter is coming的更多相关文章

  1. 2016级算法第三次上机-C.AlvinZH的奇幻猜想——三次方

    905 AlvinZH的奇幻猜想--三次方 思路 中等题.题意简单,题目说得简单,把一个数分成多个立方数的和,问最小立方数个数. 脑子转得快的马上想到贪心,从最近的三次方数往下减,反正有1^3在最后撑 ...

  2. 2016级算法第五次上机-G.ModricWang的撒币游戏

    1062 ModricWang的撒币游戏 思路 此题为2017年ACM-ICPC亚洲区域赛乌鲁木齐赛区的A题,现场94个队中有38个队做出此题.在这里作为满分以外的题,是为了让大家看一下外面一些题的风 ...

  3. 2016级算法第三次上机-B.Bamboo和巧克力工厂

    B Bamboo和巧克力工厂 分析 三条流水线的问题,依然是动态规划,但是涉及的切换种类比较多.比较易于拓展到n条流水线的方式是三层循环,外层是第k个机器手,里面两层代表可切换的流水线 核心dp语句: ...

  4. 2016级算法第六次上机-G.ModricWang likes geometry

    1116 ModricWang likes geometry 思路 难题,非常考察几何知识,放在这里作为计算几何场次的最难的题. 原题地址 原版题解 代码

  5. 2016级算法第四次上机-G.ModricWang的序列问题 II

    1021 ModricWang的序列问题II 思路 此题与上一题区别不是很大,只是增加了一个长度限制,当场通过的人数就少了很多. 大体解题过程与上一题相同.区别在于对\(f[]\) 的操作.没有长度限 ...

  6. 2016级算法第三次上机-F.ModricWang的导弹防御系统

    936 ModricWang的导弹防御系统 思路 题意即为:给出一个长度为n的序列,求出其最长不降子序列. 考虑比较平凡的DP做法: 令\(nums[i]\) 表示这个序列,\(f[x]\) 表示以第 ...

  7. 2016级算法第三次上机-E.ModricWang's Polygons

    930 ModricWang's Polygons 思路 首先要想明白,哪些多边形可能是格点正多边形? 分情况考虑: 三角形不可能,因为边长为有理数的正三角形的面积为无理数,而格点三角形的面积为有理数 ...

  8. 2016级算法第三次上机-D.双十一的抉择

    915 双十一的抉择 思路 中等题.简化题目:一共n个数,分成两组,使得两组的差最接近0,就是说要使两组数都尽可能的接近sum/2. 思路还是很混乱的,不知道如何下手,暴力也挺难的,还不能保证对.想一 ...

  9. 2016级算法第三次上机-A.Bamboo的小吃街

    A Bamboo的小吃街 分析 经典的两条流水线问题,题目描述基本类似于课件中的流水线调度,符合动态规划最优子结构性质 关键的动态规划式子为: dp[0][j] = min(dp[0][j - 1], ...

随机推荐

  1. 670. Maximum Swap 允许交换一个数 求最大值

    [抄题]: Given a non-negative integer, you could swap two digits at most once to get the maximum valued ...

  2. php实现二分查找法

    二分查找法需要数组是一个有序的数组 假设我们的数组是一个递增的数组,首先我们需要找到数组的中间位置. 一.要知道中间位置就需要知道起始位置和结束位置,然后取出中间位置的值来和我们的值做对比. 二.如果 ...

  3. spring4-2-bean配置-6-使用外部属性文件

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAk0AAAFGCAIAAAD4tzxRAAAgAElEQVR4nO2d27HsOm+tOxWn4CeXAm ...

  4. java判断一个数是否为素数[转]

    http://blog.csdn.net/lwcumt/article/details/8027586 import java.util.Scanner; //质数又称素数,是指在一个大于1的自然数中 ...

  5. Java TreeMap的排序

    TreeMap 和 HashMap 用法大致相同,但实际需求中,我们需要把一些数据进行排序:以前在项目中,从数据库查询出来的数据放在List中,顺序都还是对的,但放在HashMap中,顺序就完全乱了. ...

  6. Red Hat 6.5 Samba服务器的搭建(匿名访问,免登录)

    搭建Samba服务器是为了实现Linux共享目录之后,在Windows可以直接访问该共享目录. 现在介绍如何在红帽6.5系统中搭建Samba服务. 搭建Samba服务之前,yum源必须配置好,本地源和 ...

  7. linux c MQTT客户端实现

    linux c MQTT客户端实现 摘自:https://www.jianshu.com/p/d309de966379 一.前言:mqtt协议是轻量级的消息订阅和发布(publish/subscrib ...

  8. Jsp 的映射

    Jsp 的映射 Jsp 的映射 Jsp最佳实践 不管是jsp还是Servlet,虽然都可以开发动态Web资源,但是这两门 技术的各自特点,在长期的软件实践中,人们逐渐的把servlet作为 web应用 ...

  9. Session分布式共享 = Session + Redis + Nginx(转)

    出处:http://www.cnblogs.com/newP/p/6518918.html 一.Session 1.Session 介绍 我相信,搞Web开发的对Session一定再熟悉不过了,所以我 ...

  10. linux常用Java程序员使用命令(二)

    出品人:北极的大企鹅 1. pwd 显示当前路径 2. cd 切换目录 . .. ~ 例如: cd /root 3. ls 显示文件(夹) -l 显示详细信息 -a 显示全部,包括隐藏文件(夹)(这个 ...