紫书&&白书刷题

第九章

UVA12563 Jin Ge Jin Qu hao

这题看起来t很大,实际上经过计算可得:t <= 180 * n + 678,因此我们可用0-1背包进行处理。需要注意的是我们需要留 1s 的时间用于唱最后一首歌。

题目要求为:要在保证最多歌曲的情况下唱最长时间

我们所设定的状态方程为dp(i) := 前is所唱歌的最大数量

这里记得要把dp数组初始化为负数,这样能区别该时间点是否有人唱歌

💎UVA12563💎

int n, t;
int dp[maxn];
void solve(int kase){
    vt f(n);
    memset(dp, 0xc0, sizeof(dp)); // 初始化为 0 的话不能体现是否使用
    for (int i = 0; i < n; ++ i) std::cin >> f[i];
    int ans = 0, time = 0;
    dp[0] = 0;
    for (int i = 0; i < n; ++ i){
        for (int j = t; j >= f[i]; -- j){
            dp[j] = max(dp[j], dp[j - f[i]] + 1);
        }
    }
    // for (int i = 0; i <= t; ++ i) std::cout << dp[i] << " \n"[i == t];
    /* dp(j) := 前 j 个时间内的最值,所以我们遍历这个就可知花费时间 */
    for (int i = 0; i < t; ++ i){
        if (dp[i] >= ans) ans = dp[i], time = i;
    }
    if (ans + 1 < dp[t] or (ans + 1 == dp[t] and time + 678 < t))
        ans = dp[t], time = t;
    std::cout << "Case " << kase << ": " << ans + 1 << " " << time + 678 << "\n";
}
    

UVA11400 Lighting System Design

本题要求我们求解最小的开销。显然,低电压可以转化为高电压,且每种电压的灯泡要么全换要么不换。所以我们可以先对电压进行排序,设动态规划数组dp(i) := 其中i代表前i种灯泡的最优方案 。因此动态规划的转移方程式便为:

\[dp[i] = \min(dp[i], dp[j] + cost\cdot \sum_{k=j}^{i} cnt_k + cost_{V}) j\in [1, i) \]

💎UVA11400💎

int n;
struct node{
    int v, k, c, l;
    node () {}
    node (int a, int b, int _c, int d): v(a), k(b), c(_c), l(d) {}
    bool operator< (const node &other){
        return this->v < other.v;
    }
};
int pre[1050]; // prefix sum
int dp[1050];
node f[1050]; // lamp array 
void solve(){
    for (int i = 0; i < n; ++ i) std::cin >> f[i].v >> f[i].k >> f[i].c >> f[i].l;
    sort(f, f + n);
    pre[0] = 0;
    for (int i = 1; i <= n; ++ i) pre[i] = pre[i - 1] + f[i - 1].l;
    memset(dp, 0x3f , sizeof(dp));
    // dp(i) = min(dp(j) + (pre[i] - pre[j]) * c + node[i].k)
    dp[0] = 0;
    for (int i = 1; i <= n; ++ i){
        for (int j = 0; j < i; ++ j){
            dp[i] = min(dp[i], dp[j] + (pre[i] - pre[j]) * f[i - 1].c + f[i - 1].k);
        }
    }
    std::cout << dp[n] << "\n";
}
    

UVA11584 Partitioning by Palindromes

本题的要求是对一字符串s最少的划分,使得每个划分都为回文串。其中s的长度n不超过1000。显然,这题可以顺序dp求解。

简单的引入dp(i):=前i个字符的最优划分。于是不难写出dp[i] = min(dp[i], dp[j] + 1) | s[j+1..i]为回文串。但是要是每次转移时都用\(\mathcal{O(n)}\)的时间检查是否为回文串则会超市。

因此,我们可以利用dp\(\mathcal{O(n^2)}\)的时间预处理区间\([i,j]\)是否为回文串。之后不难写出\(\mathcal{O(n^2)}\)的动态规划。

💎UVA11400💎

int g[maxn][maxn];
int dp[1050];
void solve(){
    std::string ss;
    std::cin >> ss;
    int n = ss.length();
    memset(g, 0, sizeof(g));
    // g(i, j) : 区间(i, j)是否为回文字符串
    for (int i = n - 1; ~i; -- i){
        for (int j = i; j < n; ++ j){
            if (i == j) g[i][j] = 1;
            else if (i + 1 == j) g[i][j] = ss[i] == ss[j];
            else g[i][j] = (ss[i] == ss[j]) & g[i + 1][j - 1];
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for (int i = 1; i <= n; ++ i){
        for (int j = 0; j < i; ++ j){
            if (g[j][i - 1]) dp[i] = min(dp[i], dp[j] + 1);
        }
    }
    // for (int i = 0; i <= n; ++ i) std::cout << dp[i] << " \n"[i == n];
    std::cout << dp[n] << "\n";
}
    

UVA1626 Brackets sequence

In this problem, we need to think, how to solve the bracket matching problem. The problem has define the valid brackets. Some sequence of characters '(', ')', '[', ']' is given. We need to find the shortest possible regular brackets sequence. Since there are only a maximum of 100 characters, we can design a dynamic programing algorithm to solve it.

Let's define dp(i, j) := the minimum length of regular brackets sequence if deal the substring s[i..j] . Then we just need consider two transfer method:

  • IF s[i] is matched with s[j], we can transfer from dp[i][j] to dp[i+1][j-1]. It's not hard to think of this method.
  • IF the length of the substring S is at least 2, we can transfer it to two part divided by k. Like: dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j])

The boundary condition is: IF the length of substring S is 0, then dp[i+1][i] = 0, IF the length of substring S is 1, then dp[i][i] = 1.

💎UVA11400💎

const int maxn = 105;
int dp[maxn][maxn];
bool match(char ch1, char ch2){
    if (ch1 == '(' and ch2 == ')') return true;
    if (ch1 == '[' and ch2 == ']') return true;
    return false;
}
void print(int i, int j, string &ss){
    if (i > j) return;
    if (i == j) {
        if (ss[i] == '(' or ss[i] == ')') std::cout << "()";
        else std::cout << "[]";
        return;
    }
    int cur = dp[i][j];
    if (match(ss[i], ss[j]) and cur == dp[i + 1][j - 1]){
        std::cout << ss[i]; print(i + 1, j - 1, ss); std::cout << ss[j];
        return;
    }
    for (int k = i; k < j; ++ k){
        if (cur == dp[i][k] + dp[k + 1][j]){
            print(i, k, ss), print(k + 1, j, ss);
            return;
        }
    }
}
void solve(int t){
    string ss;
    getline(cin, ss);
    getline(cin, ss);
    int n = sz(ss);
    for (int i = 0; i <= n; ++ i)
        for (int j = 0; j <= n; ++ j) dp[i][j] = inf;
    // dp(i, j) := [i, j]
    for (int i = 0; i < n; ++ i) dp[i + 1][i] = 0;
    for (int i = 0; i < n; ++ i) dp[i][i] = 1;
    // the length of string might be zero!! 
    if (!n) { std::cout << "\n\n"; return; }
    for (int i = n - 2; ~i; -- i){
        for (int j = i + 1; j < n; ++ j){
            if (match(ss[i], ss[j])) dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
            for (int k = i; k < j; ++ k){
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
            }
        }
    }
    print(0, n - 1, ss);
    std::cout << "\n";
    if (t) std::cout << "\n";
}
signed main(){
    // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    std::cin >> t;
    getchar();
    while (t--) solve(t);
    return 0;
}
    

第十章

UVA11582 Colossal Fibonacci Numbers!

In this problem, you should calculate the value of \(f(a^b) \% n\). we define \(f(i)\) is the i'th Fibonacci number and \(f(0) = 0,f(1) = 1\). For each test case consist of three \(a, b, n\) where \(0\le a,b <2^{64}\)(\(a\) and \(b\) will not both be zero) and \(1\le n\le 1000\).

we can find \(a^b\) will very large, so we should look for some rules. Since each term of the Fibnacci series will be determined by the two adjacent terms. So we can try to find if the modulus appear also regularly.

Obviously, fib(i) % mod = (fib(i - 1) % mod + fib(i - 2) % mod) % mod, so if they recur we can determine the cyclic period of the series.

Since the modulus is n, there are at most n * n combinations of two terms.

💎UVA11400💎

const int inf = 0x3f3f3f3f;
const int maxn = 105;
int fpow(int a, int b, int mod){
    int res = 1;
    a %= mod;
    while (b){
        if (b & 1) res = (res * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    }
    return res;
}
void solve(){
    int a, b, n, T;
    std::cin >> a >> b >> n;
    vt<int> fib{0, 1 % n}; // if n equal to one, then 1 % n equal to zero.
    for (int i = 2;; ++ i){
        fib.pb((fib[i - 1] + fib[i - 2]) % n);
        if (fib[i] == fib[1] and fib[i - 1] == fib[0]){
            T = i - 1;
            break;
        }
    }
    std::cout << fib[fpow(a, b, T)] << "\n";
}
signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    std::cin >> t;
    while (t--) solve();
    return 0;
}
    
posted @ 2021-02-24 22:12  Last_Whisper  阅读(91)  评论(0编辑  收藏  举报