挑战2.3 记录结果再利用的“动态规划”

基础的动态规划算法

poj3176 Cow Bowing

没什么好说的,就是数塔问题,从最后一层往上扫,f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + a[i][j],当前状态只取决于底下两个的状态

poj2229 Sumset

求一个数分解成2的幂次之和有多少种情况。
这个问题还是需要想一想的,首先,我们应该意识到了,假如一个数是奇数,那么拆解后必含1(偶数加偶数还是偶数,2的幂次除了1都是偶数,如果没有1是不可能组成奇数的),所以当n奇数的时候,其实只要把n-1的所有情况全部加上1就行了,即f[n] = f[n - 1]。当n等于偶数时要复杂一些,但是仔细想想也能想到。当n为偶数时既可能含1也可能不含。如果有1的话,那么1一定是偶数个(奇数+奇数 = 偶数,偶数+奇数=奇数)。我们把含1和不含1的情况分开来考虑,假如含1,那么只要n-2的情况后面加上两个1即可,如果不含1呢?显然,只需要把n/2的情况每个元素都乘2就行了。举例来说 6 = (2 + 2 + 2) = (1 * 2 + 1 * 2 + 1 * 2)。所以当n为偶数的情况,f[n] = f[n/2] + f[n - 2]

代码

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;

const int maxn = 1000000 + 10;
const int mod = 1000000000;

int dp[maxn];

int main() {
    int n;
    cin >> n;
    dp[1] = 1;
    dp[2] = 2;
    for(int i = 3; i <= n; i++) {
        if(i % 2 == 1) 
            dp[i] = dp[i - 1];
        else 
            dp[i] = (dp[i - 2] + dp[i / 2]) % mod;
    }
    cout << dp[n] << endl;
    return 0;
}

poj3616 Milking Time

题意

有一头牛,每次挤奶后要休息r的时间,然后它的主人每次挤奶有一个个时间段,在这个时间段里可以让牛挤出一定的牛奶。这头牛想让自己挤出的牛奶最多,问它通过安排自己的挤奶时间能挤出的最大牛奶的量。

思路

实际上对于这头牛来说每一个时间段它都有两种选择,挤或不挤(当然也可能是还没休息好(好比技能还没冷却2333)不能挤)我们把每种状态都记录下来,对于当前的时间段,我们只要找到一个之前的时间段,距离当前时间段已经隔了时间r的并且挤的牛奶最多的状态,在此基础上再来挤。我们可以用一个数组来记录dp[i]表示到第i个时间段为止挤的最多的牛奶的量
转移状态
if(a[i].starting_hour >= a[j].ending_hour + r)
dp[i] = max(dp[i], dp[j] + a[i].efficiency)

代码

#include <iostream>
#include <algorithm>
using namespace std;

int const maxn = 1000 +10;

int dp[maxn];

struct Interval {
    int starting_hour,ending_hour,efficiency;
}a[maxn];

bool cmp(Interval a, Interval b) {
    if(a.starting_hour == b.starting_hour) {
        return a.ending_hour < b.ending_hour;
    }
    return a.starting_hour < b.starting_hour;
}

int main() {
    int n,m,r;
    cin >> n >> m >> r;
    for(int i = 1; i <= m; i++) {
        cin >> a[i].starting_hour >> a[i].ending_hour >> a[i].efficiency;
    }
    sort(a + 1, a + 1 + m, cmp);
    int Max = 0;
    for(int i = 1; i <= m; i++) dp[i] = a[i].efficiency;
    for(int i = 1; i <= m; i++) {
        for(int j = 1; j < i; j++) {
            if(a[i].starting_hour >= a[j].ending_hour + r) {
                dp[i] = max(dp[i], dp[j] + a[i].efficiency);
            }   
        }
        Max = max(Max, dp[i]);
    }
    cout << Max << endl;    
    return 0;
}

poj3280 Cheapest Palindorme

题意

给出一个字符串,再给出每个字母要删改的代价,问要把这个字符串改成一个回文串所需的最小代价

思路

这里有一个坑点,其实增加删除是一个骗局,因为一端删除和一端增加效果是一样的,所以只要把两者的最小值存下来就行了
我这里用一个二位数组来保存状态,f[i][j]表示把区间[i,j]改造成回文串所需的最小代价。我们来考虑一个问题,如果s[i] == s[j]这种情况下我们显然能得到f[i][j] = f[i + 1][j - 1](无论区间[i +1, j -1]是什么串,因为f[i + 1][j - 1]表示的是把区间[i + 1, j -1]改造成回文串的最小代价)那么如果s[i] != s[j] 呢?我们考虑当区间[i + 1][j]是回文串,对区间[i,j]来说,我只需要把s[i]删了,或者在j + 1的位置上增加s[i],就能得到一个回文串。我之前也说了实际增加和删除是一个道理也是体现在这。同理,当区间[i][j - 1]是回文串时,对区间[i,j]来说,我只要把s[j]删了,或者在i - 1的位置上加上s[j]就能得回文串。所以很容易得到f[i][j] = min(f[i + 1][j] + word[s[i]], f[i][j - 1] + word[s[j]])。

代码

#include <iostream>
using namespace std;

const int INF = 100000000;

int word_cost[500];
int dp[2000 + 10][2000 + 10];

int main() {
    int n,m;
    cin >> n >> m;
    string s;
    cin >> s;
    for(int i = 1; i <= n; i++) {
        char c;
        int x,y;
        cin >> c >> x >> y;
        word_cost[c - 'a'] = min(x,y);
    }
    for(int i = 1; i <= m; i++)
        dp[i][i] = 0;
    for(int i = 2; i <= m; i++) {
        for(int j = 0; j + i - 1 < m; j++) {
            int left = j;
            int right = j + i - 1;
            //cout << left << "," << right << endl;
            dp[left][right] = min(dp[left + 1][right] + word_cost[s[left] - 'a'],dp[left][right - 1] + word_cost[s[right] - 'a']);
            if(s[left] == s[right]) {
                dp[left][right] = dp[left + 1][right - 1];
            }
        }   
    }
    cout << dp[0][m - 1] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值