HDU 5445 Food Problem、UVa 10163 Storage Keepers、POJ 3260 The Fewest Coins(两次dp)

Food Problem

题意: 给你n种食物,m种车,每种食物有三种属性能量值t,体积u,数量v。每种车有三个属性值容量x,价格y,数量z。

         问题是在能够达到至少p能量的要求下,最小花费为多少,若大于50000则输出TAT 

分析: 两次多重背包dp 先dp出至少p能量的最小体积 然后从50000花费再dp出体积 在满足之前的最小体积下找到答案

         为啥不体积dp出花费呢? - - 看看哪个数量级大就知道了 要选取合适的状态减少复杂度 233

         记得二进制优化233

代码:

//
//  Created by TaoSama on 2015-09-25
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m, p;
int dp[60005];
//1: dp[i][j]:= i desert j energy's minimum size
//2: dp[i][j]:= i truck j cost's maximum size

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d", &n, &m, &p);

        memset(dp, 0x3f, sizeof dp);
        dp[0] = 0;
        for(int i = 1; i <= n; ++i) {
            int w, v, c; scanf("%d%d%d", &w, &v, &c);
            for(int k = 1; c > 0; c -= k, k <<= 1) {
                int mul = min(k, c);
                for(int j = p + 100; j >= mul * w; --j)
                    dp[j] = min(dp[j], dp[j - mul * w] + mul * v);
            }
        }
        int V = INF;
        for(int i = p; i <= p + 100; ++i) V = min(V, dp[i]);
//      printf("V: %d\n", V);

        memset(dp, 0, sizeof dp);
        int ans = INF;
        for(int i = 1; i <= m; ++i) {
            int v, w, c; scanf("%d%d%d", &v, &w, &c);
            for(int k = 1; c > 0; c -= k, k <<= 1) {
                int mul = min(k, c);
                for(int j = 50000; j >= mul * w; --j) {
                    dp[j] = max(dp[j], dp[j - mul * w] + mul * v);
                    if(dp[j] >= V) ans = min(ans, j);
                }
            }
        }
        if(ans == INF) puts("TAT");
        else printf("%d\n", ans);
    }
    return 0;
}

Storage Keepers

题意: 有n<100个仓库  m<=30个管理员  每个管理员有一个能力值P(接下来的一行有m个数,表示每个管理员的能力值)
          每个仓库只能由一个管理员看管,但是每个管理员可以看管k个仓库(但是这个仓库分配到的安全值只有p/k,k=0,1,...
          每个月公司都要给看管员工资,雇用的管理员的工资即为他们的能力值p和,求使每个仓库的安全值最高的前提下 使的工资总和最小。
          输出最大安全值,并且输出最少的花费。

分析: 两次类似于多重背包的dp 状态见代码 第一次dp出安全值 第二次再在这个状态下找到最少花费  注意初始化好好想想啊

代码:

//
//  Created by TaoSama on 2015-08-12
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m, p[35];
int f[35][105], g[35][105];
//dp[i][j]:= i people look after j storages max safe, min wage

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &m, &n) == 2 && (n || m)) {
        for(int i = 1; i <= n; ++i) scanf("%d", p + i);

        memset(f, 0, sizeof f);
        f[0][0] = INF; //initialization is so difficult!
        for(int i = 1; i <= n; ++i) {
            for(int j = 0; j <= m; ++j) {
                f[i][j] = f[i - 1][j]; //zero exception
                for(int k = 1; k <= p[i] && k <= j; ++k)
                    f[i][j] = max(f[i][j], min(f[i - 1][j - k], p[i] / k));
            }
        }
        int L = f[n][m];
        if(!L) {printf("0 0\n"); continue;}

        memset(g, 0x3f, sizeof g);
        g[0][0] = 0;
        for(int i = 1; i <= n; ++i) {
            for(int j = 0; j <= m; ++j) {
                g[i][j] = g[i - 1][j];
                for(int k = 1; k <= p[i] && k <= j; ++k) {
                    int s = p[i] / k;  //at least
                    if(s >= f[n][m]) g[i][j] = min(g[i][j], g[i - 1][j - k] + p[i]);
                }
            }
        }
        int Y = g[n][m];
        printf("%d %d\n", L, Y);
    }
    return 0;
}

The Fewest Coins

题意: 给出钱币的种类数和总的购买值  然后给出每种钱币的价值与数量
         老板也是每种钱币都拥有  但是没有数量限制  购买东西的时候 价值超过给定价值的话 老板会找钱
         求最小的交流钱币的数量
分析: 两次dp啦, 第一次多重背包 求出用了多少硬币  然后第二次完全背包求出找了多少硬币 然后求个最小值
         最重要的是上界的处理。可以注意到,上界为maxw*maxw+m(maxw最大面额的纸币),也就是24400元。
         贴个证明:
         如果买家的付款数大于了maxw*maxw+m,即付硬币的数目大于了maxw,根据鸽笼原理,至少有两个的和对maxw取模的值相等
         也就是说,这部分硬币能够用更少的maxw来代替。证毕。
         真心不懂证明也没关系,记住结论就好了,真心不想记结论也没关系,数组开大一点就好了。
代码:
//
//
//
//  Created by TaoSama on 2015-03-30
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;

int n, t, dpp[25000], dpn[15000], c[105], v[105];

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//	freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    cin >> n >> t;
    for(int i = 1; i <= n; ++i) cin >> v[i];
    for(int i = 1; i <= n; ++i) cin >> c[i];

    memset(dpp, 0x3f, sizeof dpp);
    dpp[0] = 0;
    for(int i = 1; i <= n; ++i) {
        int k = 1;
        while(c[i] > 0) {
            int t = min(c[i], k);
            for(int j = t + 120 * 120; j >= t * v[i]; --j)
                dpp[j] = min(dpp[j], dpp[j - t * v[i]] + t);
            c[i] -= k; k <<= 1;
        }
    }
    /*for(int i = 0; i <= t + 120 * 120; ++i)
        if(dpp[i] != INF) printf("dpp[%d]: %d\n", i, dpp[i]);*/

    memset(dpn, 0x3f, sizeof dpn);
    dpn[0] = 0;
    for(int i = 1; i <= n; ++i)
        for(int j = v[i]; j <= 120 * 120; ++j)
            dpn[j] = min(dpn[j], dpn[j - v[i]] + 1);
    /*for(int i = 0; i <= 120 * 120; ++i)
        if(dpn[i] != INF) printf("dpn[%d]: %d\n", i, dpn[i]);*/

    int ans = INF;
    for(int i = 0; i <= 120 * 120; ++i)
        ans = min(ans, dpp[t + i] + dpn[i]);

    if(ans == INF) ans = -1;
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(HDU 5445 Food Problem、UVa 10163 Storage Keepers、POJ 3260 The Fewest Coins(两次dp))