标签: 动态规划

[BJWC2010]取数游戏 题解

[BJWC2010]取数游戏 题解

题目地址:洛谷:【P4411】[BJWC2010]取数游戏 – 洛谷、BZOJ 

[BZOJ3208]花神的秒题计划Ⅰ 题解

[BZOJ3208]花神的秒题计划Ⅰ 题解

题目地址:BZOJ:Problem 3208. — 花神的秒题计划Ⅰ 题目描述 

[POI2015]LAS-Łasuchy 题解

[POI2015]LAS-Łasuchy 题解

题目地址:洛谷:【P3584】[POI2015]LAS – 洛谷、BZOJ:Problem 3749. — [POI2015]Łasuchy

题目描述

圆桌上摆放着n份食物,围成一圈,第i份食物所含热量为c[i]。 相邻两份食物之间坐着一个人,共有n个人。每个人有两种选择,吃自己左边或者右边的食物。如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量。 假如某个人改变自己的选择后(其他n-1个人的选择不变),可以使自己获得比原先更多的热量,那么这个人会不满意。 请你给每个人指定应该吃哪一份食物,使得所有人都能够满意。

输入输出格式

输入格式:
第一行一个整数n(2<=n<=1000000),表示食物的数量(即人数)。食物和人都从1~n编号。
第二行包含n个整数c[1],c[2],…,c[n] (1<=c[i]<=10^9)。
假设第i个人(1<=i<n)左边是第i份食物,右边是第i+1份食物;而第n个人左边是第n份食物,右边是第1份食物。

输出格式:
如果不存在这样的方案,仅输出一行NIE。
如果存在这样的方案,输出一行共n个整数,第i个整数表示第i个人选择的食物的编号。如果有多组这样的方案,输出任意一个即可。

输入输出样例

输入样例#1:

5
5 3 7 2 9

输出样例#1:

2 3 3 5 1 

题解

是一个动态规划的判定问题。我们可以枚举第一个食物被左边/右边/两边/没被吃的情况,然后通过递推获得后面的食物的某个状态的可行性。
例如,当前一个食物被左边的人吃了,显然当前食物是必须让当前的左边的人吃掉的,而枚举剩下的情况,就是右边的人吃不吃,从而判断左边的人吃当前食物对他来说是否是最优的。
我们可以在DP的过程中记下上一个状态,在输出方案的时候倒推统计每个人吃的是什么食物,这样就可以输出方案了。
这个DP的复杂度是O(n)的。

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 1000005;

// 1: left 2: right 3: both 4: neither
int n, dp[MAXN][5];
LL c[MAXN];

inline bool caldp(int s) {
    memset(dp, 0, sizeof(dp));
    dp[1][s] = 1;
    for(int i = 2; i <= n + 1; i++) {
        if(dp[i - 1][1] && c[i - 1] <= c[i] * 2) dp[i][1] = 1;
        if(dp[i - 1][1] && c[i - 1] <= c[i]) dp[i][3] = 1;
        if(dp[i - 1][2] && c[i - 1] * 2 >= c[i]) dp[i][2] = 2;
        if(dp[i - 1][2] && c[i - 1] >= c[i]) dp[i][4] = 2;
        if(dp[i - 1][3] && c[i - 1] >= c[i]) dp[i][2] = 3;
        if(dp[i - 1][3] && c[i - 1] >= c[i] * 2) dp[i][4] = 3;
        if(dp[i - 1][4] && c[i - 1] <= c[i]) dp[i][1] = 4;
        if(dp[i - 1][4] && c[i - 1] * 2 <= c[i]) dp[i][3] = 4;
    }
    return dp[n + 1][s];
}

int ans[MAXN];

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        c[i] = readint();
    }
    c[n + 1] = c[1];
    bool success = false;
    for(int i = 1; i <= 4; i++) {
        if(caldp(i)) {
            int type = i;
            for(int i = n + 1; i >= 1; i--) {
                if(type == 1) ans[i - 1] = (i - 1) % n + 1;
                if(type == 2) ans[i] = (i - 1) % n + 1;
                if(type == 3) ans[i - 1] = ans[i] = (i - 1) % n + 1;
                type = dp[i][type];
            }
            for(int i = 1; i <= n; i++) {
                printf("%d ", ans[i]);
            }
            return 0;
        }
    }
    puts("NIE");
    return 0;
}
[SCOI2009]windy数 题解

[SCOI2009]windy数 题解

题目地址:洛谷:【P2657】[SCOI2009]windy数 – 洛谷、BZ 

[BZOJ3209]花神的数论题 题解

[BZOJ3209]花神的数论题 题解

题目地址:洛谷:【P4317】花神的数论题 – 洛谷、BZOJ:Problem 

[HAOI2010]最长公共子序列 题解

[HAOI2010]最长公共子序列 题解

题目地址:洛谷:【P2516】[HAOI2010]最长公共子序列 – 洛谷、BZOJ:Problem 2423. — [HAOI2010]最长公共子序列

题目描述

字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij = yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。

题意简述

求两个串LCS长度及个数。

输入输出格式

输入格式:
第1行为第1个字符序列,都是大写字母组成,以”.”结束。长度小于5000。
第2行为第2个字符序列,都是大写字母组成,以”.”结束,长度小于5000。

输出格式:
第1行输出上述两个最长公共子序列的长度。
第2行输出所有可能出现的最长公共子序列个数,答案可能很大,只要将答案对100,000,000求余即可。

输入输出样例

输入样例#1:

ABCBDAB.
BACBBD.

输出样例#1:

4
7

题解

注意到本题卡空间,要用到滚动数组优化空间。
这里可以把统计方案数跟DP一起做,LCS依然是那个经典的DP问题,只不过这里把第一维给滚动掉了。至于方案数,检查该状态从哪些其他状态转移而来,加上这些状态的方案数即可。如果s1[i]!=s2[j],且dp[i][j]==dp[i-1][j-1],则要减去dp[i-1][j-1]对应的方案数,因为这个方案数会在dp[i][j-1]和dp[i-1][j]被计算两次,算重了。

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

const int MAXN = 5005, MO = 1e8;

int n, m, dp[2][MAXN], dp2[2][MAXN];
char s1[MAXN], s2[MAXN];

int main() {
    scanf("%s%s", s1 + 1, s2 + 1);
    n = strlen(s1 + 1) - 1; m = strlen(s2 + 1) - 1;
    for(int i = 0; i <= m; i++) dp2[0][i] = 1;
    dp2[1][0] = 1;
    int p = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            dp[p][j] = std::max(dp[p ^ 1][j], dp[p][j - 1]);
            dp2[p][j] = 0;
            if(s1[i] == s2[j]) {
                dp[p][j] = std::max(dp[p][j], dp[p ^ 1][j - 1] + 1);
            }
            if(s1[i] == s2[j] && dp[p][j] == dp[p ^ 1][j - 1] + 1) {
                dp2[p][j] = (dp2[p][j] + dp2[p ^ 1][j - 1]) % MO;
            }
            if(dp[p][j] == dp[p ^ 1][j]) {
                dp2[p][j] = (dp2[p][j] + dp2[p ^ 1][j]) % MO;
            }
            if(dp[p][j] == dp[p][j - 1]) {
                dp2[p][j] = (dp2[p][j] + dp2[p][j - 1]) % MO;
            }
            if(dp[p][j] == dp[p ^ 1][j - 1]) {
                dp2[p][j] = ((dp2[p][j] - dp2[p ^ 1][j - 1]) % MO + MO) % MO;
            }
        }
        p ^= 1;
    }
    printf("%d\n%d", dp[p ^ 1][m], dp2[p ^ 1][m]);
    return 0;
}
[SDOI2009]HH去散步 题解

[SDOI2009]HH去散步 题解

题目地址:洛谷:【P2151】[SDOI2009]HH去散步 – 洛谷、BZO 

[SDOI2014]数数 题解

[SDOI2014]数数 题解

题目地址:洛谷:【P3311】[SDOI2014]数数 – 洛谷、BZOJ:P 

[POI2005]BAN-Bank Notes 题解

[POI2005]BAN-Bank Notes 题解

题目地址:洛谷:【P3423】[POI2005]BAN-Bank Notes – 洛谷、BZOJ:Problem 1531. — [POI2005]Bank notes

注:截止本文发布时,洛谷上的该题仍然0AC,怀疑数据出现问题。

题目描述

Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,…, bn. 但是每种硬币有数量限制,现在我们想要凑出面值k求最少要用多少个硬币.

输入输出格式

输入格式:
第一行一个数 n, 1 <= n <= 200. 接下来一行 n 个整数b1, b2,…, bn, 1 <= b1 < b2 < … < b n <= 20 000, 第三行 n 个整数c1, c2,…, cn, 1 <= ci <= 20 000, 表示每种硬币的个数.最后一行一个数k – 表示要凑的面值数量, 1 <= k <= 20 000.

输出格式:
第一行一个数表示最少需要付的硬币数

输入输出样例

输入样例#1:

3
2 3 5
2 2 1
10

输出样例#1:

3
1 1 1

题解

裸的部分背包,可以做一个求最小值的部分背包即可。对于输出方案,我们可以先二进制拆分成01背包问题,再记录下01背包的转移过程,倒推统计每个物品是否被选中即可。
复杂度O(nk \log n)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 10005, INF = 1e9;

int n, b[MAXN], c[MAXN], v[MAXN], w[MAXN], bel[MAXN], cnt[MAXN], tot, k, dp[20005];
bool vis[MAXN][20005];

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        b[i] = readint();
    }
    for(int i = 1; i <= n; i++) {
        c[i] = readint();
    }
    k = readint();
    for(int i = 1; i <= n; i++) {
        for(int siz = 1; c[i]; siz <<= 1) {
            siz = std::min(siz, c[i]); c[i] -= siz;
            tot++; w[tot] = siz; v[tot] = b[i] * siz; bel[tot] = i;
        }
    }
    for(int i = 1; i <= k; i++) {
        dp[i] = INF;
    }
    for(int i = 1; i <= tot; i++) {
        for(int j = k; j >= v[i]; j--) {
            if(dp[j] > dp[j - v[i]] + w[i]) {
                dp[j] = dp[j - v[i]] + w[i];
                vis[i][j] = true;
            }
        }
    }
    printf("%d\n", dp[k]);
    for(int i = tot; i >= 1; i--) {
        if(vis[i][k]) {
            cnt[bel[i]] += w[i];
            k -= v[i];
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", cnt[i]);
    }
    return 0;
}
[AHOI2008]逆序对 题解

[AHOI2008]逆序对 题解

题目地址:洛谷:【P4280】[AHOI2008]逆序对 – 洛谷、BZOJ: