“第六周 acm 刷题总结 线性DP”

前期刷题整理:
1.反恐训练营
当今国际反恐形势很严峻,特别是美国“9.11事件”以后,国际恐怖势力更是有恃无恐,制造了多起骇人听闻的恐怖事件。基于此,各国都十分担心恐怖势力会对本国社会造成的不稳定,于是纷纷在本国的军队、警察队伍中开展了反恐训练。作为反恐立场坚定的大国,中国也十分重视在人民解放军、武装警察部队、人民警察队伍中反恐训练,还专门成立了反恐特警队。

炜炜是反恐特警队的一名新队员,现在正在接受培训。这几天刚好是射击训练第二阶段——实弹应变训练的日子,此前的第一阶段里,炜炜经过努力,已经将自己训练成为一个百发百中的神抢手了!这次,他将背着国产最新型12.7mm重型狙击枪进行训练比赛。

这次训练比赛的规则是这样的:

1、每个队员从出发点开始,沿着一条唯一的笔直道路跑直到终点,途中不允许往回跑,否则将被取消比赛资格。
2、出发前,每个队员的枪膛内都被装了顺序一样的、用小写英文字母标明类型的子弹序列,每位队员被告知这一序列的信息;同时,每位队员也被告知恐怖分子即将出现的序列和类型(同样用小写英文字母标明类型)。
3、在跑动的过程中,若发现“恐怖分子”,特警队员可以选择用枪击毙他,来得到写在“恐怖分子”胸前的得分,但是前提是他使用的子弹类型必须和“恐怖分子”类型相同,否则,即使击毙了“恐怖分子”,也得不到分数;当然选择不击毙他也是可以的,这样他不会从那个“恐怖分子”身上得到分数。
4、允许特警队员放空枪,这样可以消耗掉型号不对的子弹而不至于杀死“恐怖分子”(当然每个特警队员都不会愚蠢到不装消音装置就放空枪,以至于吓跑“恐怖分子”),等待枪口出现正确型号的子弹击毙他得分。

这里,我们假定:
1、对于每个队员,途中出现恐怖分子的地点、时间、类型也是完全一样的。
2、每颗子弹都是质量合格的,都可以发挥杀伤效力
3、由于队员各个都是神枪手,一旦他选择了正确的子弹,向目标射击,目标100%被爆头
4、每个队员的记忆力超强,能记住所有子弹序列信息和恐怖分子序列信息。
5、每个队员体力足够好,能跑完全程,并做他想要做的
6、“恐怖分子”是不动的,小范围内不存在多于一个的恐怖分子;

炜炜需要你的帮助,告诉他如何做,才能得到最高的分数。现在如果告诉你出发时枪膛内子弹的序号和型号、恐怖分子出现的序号和类型,你能告诉炜炜他最多能得到多少分数吗?

Input
输入数据的第一行有一个整数N表示子弹和恐怖分子的类型数。随后的一行是各种恐怖分子类型的一行字母,两个字母之间没有任何字符。接下来的一行是击毙上一行对应位置恐怖分子类型的得分数,每个分数之间恰有一个空格。第三第四行分别表示开始时枪膛内子弹的序列(左边的先打出)和恐怖分子出现的序列(左边的先出现),字母之间都没有任何字符。
每个测试数据之间没有空格和空行。你的程序必须通过全部测试数据,才能被判为AC。
Output
对于每一个测试数据,输出炜炜最多能得到的分数。
Sample Input

3
abc
1 1 1
abc
ccc
3
abc
1 1 1
ccc
aba

Sample Output

1
0

本题用第几颗子弹待发射作为不同阶段,如果本阶段子弹与恐怖分子对应相遇那么这个状态的得分是上一阶段在遇到这个恐怖分子之前一个状态的得分加上这个恐怖分子的分数,dp[i][j] = dp[i - 1][j - 1] + score[j];
本阶段 的子弹与恐怖分子尚未相遇的状态的值要 取上一阶段遇到这个恐怖分子的值和本阶段尚未遇到此恐怖分子的值的最大值。dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
代码如下:

 #include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 100005
int dp[2][MAX];
char typ[MAX];
int score[MAX];
char kong[MAX];
char dan[MAX];

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int T;
	while (cin >> T)
	{	
		memset(dp, 0, sizeof(dp));
			cin >> typ;
		for (int i = 0; i < T; i++)
			cin >> score[typ[i]];
			cin >> kong + 1;cin >> dan + 1;
			int l1 = strlen(dan + 1), l2 = strlen(kong + 1);
			
		for (int i = 1; i <= l1; i++)
			for (int j = 1; j <= l2; j++)

				if (dan[i] == kong[j]) dp[i & 1][j] = dp[(i - 1) & 1][j - 1] + score[kong[j]];
				else dp[i & 1][j] = max(dp[i & 1][j - 1], dp[(i - 1) & 1][j]);
	
		cout << dp[l1 & 1][l2] << endl;
	}
	return 0;
}
  1. D - Tickets
    Jesus, what a great movie! Thousands of people are rushing to the cinema. However, this is really a tuff time for Joe who sells the film tickets. He is wandering when could he go back home as early as possible.
    A good approach, reducing the total time of tickets selling, is let adjacent people buy tickets together. As the restriction of the Ticket Seller Machine, Joe can sell a single ticket or two adjacent tickets at a time.
    Since you are the great JESUS, you know exactly how much time needed for every person to buy a single ticket or two tickets for him/her. Could you so kind to tell poor Joe at what time could he go back home as early as possible? If so, I guess Joe would full of appreciation for your help.
    Input
    There are N(1<=N<=10) different scenarios, each scenario consists of 3 lines:

    1. An integer K(1<=K<=2000) representing the total number of people;
    2. K integer numbers(0s<=Si<=25s) representing the time consumed to buy a ticket for each person;
    3. (K-1) integer numbers(0s<=Di<=50s) representing the time needed for two adjacent people to buy two tickets together.
      Output
      For every scenario, please tell Joe at what time could he go back home as early as possible. Every day Joe started his work at 08:00:00 am. The format of time is HH:MM:SS am|pm.
      Sample Input

    2
    2
    20 25
    40
    1
    8

Sample Output

08:00:40 am
08:00:08 am

本题每一个状态取前i个人抱团和不抱团用时较少的一个。方程:dp[i] = min(single[i] + dp[i - 1], group[i] + dp[i - 2]);

代码如下:

#include <stdio.h>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;

const int M = 10054;
int single[M], group[M];
int dp[M];

int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
        memset(single, 0, sizeof(single));
                memset(group, 0, sizeof(group));
                memset(dp, 0, sizeof(dp));
                int n;
                scanf("%d", &n);
                for (int i = 1; i <= n; i++)
                        scanf("%d", &single[i]);
                for (int i = 2; i <= n; i++)
                        scanf("%d", &group[i]);
                dp[1] = single[1];
                dp[2] = min(single[1] + single[2], group[2]);
                for (int i = 3; i <= n; i++)
                        dp[i] = min(single[i] + dp[i - 1], group[i] + dp[i - 2]);
                int res = dp[n];
                int h = 8, m, s;
                m = res / 60;
                s = res % 60;
                h += m / 60;
        m %= 60;
                string tail;
        if (h >= 12) {
                        if (h > 12) h %= 12;
            tail = "pm";
                }
                else {
                tail = "am";
                }
                printf("%02d:%02d:%02d %s\n", h, m, s, tail.c_str());
        }
        return 0;
}
  1. G - How many ways

    这是一个简单的生存游戏,你控制一个机器人从一个棋盘的起始点(1,1)走到棋盘的终点(n,m)。游戏的规则描述如下:
    1.机器人一开始在棋盘的起始点并有起始点所标有的能量。
    2.机器人只能向右或者向下走,并且每走一步消耗一单位能量。
    3.机器人不能在原地停留。
    4.当机器人选择了一条可行路径后,当他走到这条路径的终点时,他将只有终点所标记的能量。

如上图,机器人一开始在(1,1)点,并拥有4单位能量,蓝色方块表示他所能到达的点,如果他在这次路径选择中选择的终点是(2,4)

点,当他到达(2,4)点时将拥有1单位的能量,并开始下一次路径选择,直到到达(6,6)点。
我们的问题是机器人有多少种方式从起点走到终点。这可能是一个很大的数,输出的结果对10000取模。 

Input
第一行输入一个整数T,表示数据的组数。
对于每一组数据第一行输入两个整数n,m(1 <= n,m <= 100)。表示棋盘的大小。接下来输入n行,每行m个整数e(0 <= e < 20)。
Output
对于每一组数据输出方式总数对10000取模的结果.
Sample Input

1
6 6
4 5 6 6 4 3
2 2 3 1 7 2
1 1 4 6 2 7
5 8 4 3 9 5
7 6 6 2 1 5
3 1 1 3 7 2

Sample Output

3948

这道题就把到每一个点的方式总数都存下来,每一个点的方式总数都是前面所有能走到这个点的方式总数的和。状态转移方程就是
dp[x][y] = ∑(0->i, 0->j) dp[x - i] [x - j]; 正着算倒着算都可,这种是枚举每个状态的每一 个上级状态,把他们的值累加,正着算的话就是
枚举当前状态的每一个下一级状态,下一级状态更新为加上当前状态之后的值。一遍下来也和上面操作达到同一个结果。后者代码如下:
初始化dp[0][0]作为起点只有一种到达方式。

#include <iostream>
using namespace std;
int mmap[205][205], dp[205][204], m, n;
void list(int a, int  b) {
                      int i , j;
        for (i = 0; i <= mmap[a][b]; i++) {
        for (j = 0; j + i <= mmap[a][b]; j++) {
                        if (!(i || j)) continue;
                        if (a + i >= m || b + j >= n) continue;
                        dp[a + i][b + j] += dp[a][b];
                        dp[a + i][b + j] %= 10000;

                }
        }
}

int main() {
        int t;
        cin >> t;
        while (t--) {
                cin >> m >> n;
        for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                cin >> mmap[i][j];
                                dp[i][j] = 0;
                        }
                }
                dp[0][0] = 1;
                for (int i = 0; i < m; i++)
                        for (int j = 0; j < n; j++) {
                list(i, j);
                        }
                cout << dp[m - 1][n - 1] << endl;
        }
        return 0;
}
  1. H - Worm

    自从见识了平安夜苹果的涨价后,Lele就在他家门口水平种了一排苹果树,共有N棵。

    突然Lele发现在左起第P棵树上(从1开始计数)有一条毛毛虫。为了看到毛毛虫变蝴蝶的过程,Lele在苹果树旁观察了很久。虽然没有看到蝴蝶,但Lele发现了一个规律:每过1分钟,毛毛虫会随机从一棵树爬到相邻的一棵树上。

    比如刚开始毛毛虫在第2棵树上,过1分钟后,毛毛虫可能会在第1棵树上或者第3棵树上。如果刚开始时毛毛虫在第1棵树上,过1分钟以后,毛毛虫一定会在第2棵树上。

    现在告诉你苹果树的数目N,以及毛毛刚开始所在的位置P,请问,在M分钟后,毛毛虫到达第T棵树,一共有多少种行走方案数。
    Input
    本题目包含多组测试,请处理到文件结束(EOF)。
    每组测试占一行,包括四个正整数N,P,M,T(含义见题目描述,0<N,P,M,T<100)
    Output
    对于每组数据,在一行里输出一共的方案数。
    题目数据保证答案小于10^9
    Sample Input

    3 2 4 2
    3 2 3 2

Sample Output

4
0

Hint

第一组测试中有以下四种走法:
2->1->2->1->2
2->1->2->3->2
2->3->2->1->2
2->3->2->3->2

这道题就是注意毛毛虫只能从上一分钟的左邻的树或者右邻的树转移到当前的树。
状态转移方程就是dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1]

#include <iostream>
using namespace std;

int main() {
        int N, P, M, T;
        while (cin >> N >> P >> M >> T) {
        int dp[2][104] = { 0 };
                dp[0][P] = 1;
                for (int i = 1; i <= M; i++) {
                for ( int j = 1; j <= N; j++) {
                dp[i & 1][j] = dp[(i - 1) & 1][j - 1] + dp[(i - 1) & 1][j + 1];
                        }
                }
                cout << dp[M & 1][T] << endl;
        }
        return 0;
}

5 Z - Flowers
We saw the little game Marmot made for Mole’s lunch. Now it’s Marmot’s dinner time and, as we all know, Marmot eats flowers. At every dinner he eats some red and white flowers. Therefore a dinner can be represented as a sequence of several flowers, some of them white and some of them red.

But, for a dinner to be tasty, there is a rule: Marmot wants to eat white flowers only in groups of size k.

Now Marmot wonders in how many ways he can eat between a and b flowers. As the number of ways could be very large, print it modulo 1000000007 (109 + 7).

Input

Input contains several test cases.

The first line contains two integers t and k (1 ≤ t, k ≤ 105), where t represents the number of test cases.

The next t lines contain two integers ai and bi (1 ≤ ai ≤ bi ≤ 105), describing the i-th test.

Output

Print t lines to the standard output. The i-th line should contain the number of ways in which Marmot can eat between ai and bi flowers at dinner modulo 1000000007 (109 + 7).

Examples
Input

3 2
1 3
2 3
4 4

Output

6
5
5

Note

    For K = 2 and length 1 Marmot can eat (R).
    For K = 2 and length 2 Marmot can eat (RR) and (WW).
    For K = 2 and length 3 Marmot can eat (RRR), (RWW) and (WWR).
    For K = 2 and length 4 Marmot can eat, for example, (WWWW) or (RWWR), but for example he can't eat (WWWR).

 这道题当前状态吃红花的方式数等于没 吃红花之前吃红花方式和吃白花方式的和,吃白花方式数等于没吃这些白花之前吃两种花的方式和。
 用第二个给下标0表示吃红花,1表示吃白花。
 状态转移方程: dp[i][0] = (dp[i - 1][0] + dp[i - 1][1])            if (i >= k) dp[i][1] = (dp[i - k][0] + dp[i - k][1])
 代码如下:
#include <stdio.h>
using namespace std;
#define sw(a, b) {\
        __typeof(a) t = b;\
                b = a; a = t;\
}
int dp[100060][2];
int sum[100020];
const int mod = 1e9 + 7;
int main() {
        int t, k;
        scanf("%d%d", &t, &k);
        dp[0][1] = 1;
        for (int i = 1; i <= 100000; i++) {
        dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
                if (i >= k) dp[i][1] = (dp[i - k][0] + dp[i - k][1]) % mod;
                else dp[i][1] = 0;
                sum[i] = (sum[i - 1] + dp[i][1]) % mod;
                sum[i] = (sum[i] + dp[i][0]) % mod;
        }

        while (t--) {
                int a, b;
        scanf("%d%d", &a, &b);
                if (a > b) sw(a,b);
     printf("%d\n", ((sum[b] - sum[a - 1]) % mod + mod) % mod);
        }

        return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值