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: 403884Created 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 1000007int 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个连续野人士兵的方案数。*/

转载于:https://www.cnblogs.com/AlvinZH/p/7977917.html

2016级算法第三次上机-G.Winter is coming相关推荐

  1. 2016级算法第六次上机-A.Bamboo之寻找小金刚

    Bamboo之寻找小金刚 分析 可以抽象为许多连续线段,分别计数左拐和右拐的个数.考察叉积的基础应用. 假设ABC三点构成一个夹角∠ABC,B就是拐点,AC是辅助形成夹角.考虑线段AB和BC形成的向量 ...

  2. 2016级算法第五次上机-E.AlvinZH的学霸养成记IV

    1039 AlvinZH的学霸养成记IV 思路 难题,最大二分图匹配. 难点在于如何转化问题,n对n,一个只能攻击一个,判断是否存在一种攻击方案我方不死团灭对方.可以想到把所有随从看作点,对于可攻击的 ...

  3. 2016级算法第六次上机-D.AlvinZH的学霸养成记V

    1081 AlvinZH的学霸养成记V 思路 中等题,计算几何. 这是一个排序问题,按极角排序.可以转化为叉积的应用,对于点A和B,通过叉积可以判断角度大小,共线时再判断距离. 叉积的应用.OA × ...

  4. 2016级算法第四次上机-B ModricWang的序列问题

    1019 ModricWang的序列问题 思路 此题题意非常清晰,给定一个序列,求出最长上升子序列的长度.从数据规模来看,需要\(O(nlogn)\) 的算法. \(O(nlongn)\) 求最长上升 ...

  5. 2016级算法第五次上机-C.Bamboo和Coco

    1064 Bamboo和"Coco" 分析题意 每个亡灵至少一个花瓣,相邻的亡灵中思念值高的要获得的花瓣高(思念值相等是不需要花瓣一样多的).主要考贪心思路,为了使得花瓣总量最少, ...

  6. 2016级算法第四次上机-C.AlvinZH的1021实验

    975 AlvinZH的1021实验 思路 贪心,简单题. 题目已经说明有且只有一种方法表示所求数,简单列举几项可以发现只由前i个砝码会可以表示[1,∑Wi]的所有数的.先找到最大需要的砝码Wi,问题 ...

  7. P3-2017级算法第三次上机 B SkyLee逛漫展

    题目描述 求数组中数字xx所在的位置(保证数组严格递增) 输入 多组数据输入 第一行一个整数nn,为数组元素个数(1<n<=1e6) 第二行nn个整数,为数组元素 第三行一个整数,为请求查 ...

  8. 2016级算法期末上机-H.难题·AlvinZH's Fight with DDLs III

    1119 AlvinZH's Fight with DDLs III 思路 难题,最小点覆盖. 分析题意,某一个任务,既可以在笔记本A的 \(a\) 模式下完成,也可以在笔记本B的 \(b\) 模式下 ...

  9. 2016级算法期末上机-F.中等·AlvinZH's Fight with DDLs II

    1118 AlvinZH's Fight with DDLs II 思路 中等题,贪心. 理解题意,每次攻击中,可以使某个敌人生命值-1,自己生命值减去∑存活敌人总攻击力. 贪心思想,血量少攻击高的要 ...

最新文章

  1. nginx rewrite 指令last break区别最详细的解释
  2. oracle ORACLE_SID使用上的意义
  3. 车小米O2O保养平台搭建完毕
  4. 如何构建一套高性能、高可用性、低成本的视频处理系统?
  5. 1.78版正式发布,不好意思,让大家多等了一天
  6. 阻止中文输入法输入拼音的时候触发input事件
  7. php switch goto,PHP goto语句用法实例
  8. 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败
  9. Web负载均衡与分布式架构
  10. distri.lua的web运维工具
  11. UISegmentedControl触发事件的错误
  12. 用C#在Visual Studio写Javascript单元测试
  13. 同济大学数学系高等数学第6版笔记和课后答案
  14. 光学软件市场现状研究分析报告-
  15. 信号傅里叶变换后的实数和虚数部分理解
  16. 三种Lp范数距离定义
  17. 刷题刷题(个人记录)
  18. 日本某地发生了一件谋杀案
  19. Chapter 8 (Bayesian Statistical Inference): Bayesian Inference and the Posterior Distribution
  20. 特斯拉调整电动汽车售价 Model S、Model X国内售价也有上调

热门文章

  1. 实例解析Java反射
  2. FastDFS配置文件详解
  3. 骨传导蓝牙耳机什么牌子好?骨传导耳机品牌推荐
  4. 基于ESP8266网页控制RGB三色灯的颜色
  5. PHP 替换原则,里氏替换原则是什么?
  6. 十二、G1垃圾收集器
  7. 已知波特率 生成c语言,C语言中怎么设置波特率
  8. 1609_PC汇编语言_汇编简介以及first例程后续
  9. 2021年3月教育行业网络关注度分析报告
  10. 阿里巴巴校园招聘 —— 灵犀游戏开发测试岗笔试题目总结(菜鸡版解析)涉及知识点——十字链表、线程与堆栈、FTP、Telnet、红黑树、哈夫曼树、平衡二叉树、乐观锁、悲观锁、HTTP、NIM游戏