小白书部分动规习题

小白书动规书后习题:
UVA 116 
简单记忆化dp,一个点可以更新三个点
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 100 + 5;
#define LL long long
#define inf (1000000007)
LL dp[MAXN][MAXN];
int data[MAXN][MAXN];
int path[MAXN][MAXN];
int out[MAXN];
int main()
{
// freopen("UVA 116.in", "r", stdin);
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF && n + m){
        for(int i = 1 ; i <= n ; i++)   for(int j = 1 ; j <= m ; j++)   scanf("%d", &data[i][j]);
        memset(dp, 0, sizeof(dp));
        for(int j = m ; j >= 1 ; j--){
            for(int i = 1 ; i <= n ; i++){
                if(j == m)  dp[i][j] = data[i][j], path[i][j] = -2;
                else{
                    dp[i][j] = inf;
                    if(i == n && dp[i][j] > dp[1][j + 1])   dp[i][j] = dp[1][j + 1], path[i][j] = 1 - n;
                    if(i > 1 && dp[i][j] > dp[i - 1][j + 1])    dp[i][j] = dp[i - 1][j + 1], path[i][j] = -1;
                    if(dp[i][j] > dp[i][j + 1])    dp[i][j] = dp[i][j + 1], path[i][j] = 0;
                    if(i < n && dp[i][j] > dp[i + 1][j + 1])    dp[i][j] = dp[i + 1][j + 1], path[i][j] = 1;
                    if(i == 1 && dp[i][j] > dp[n][j + 1])   dp[i][j] = dp[n][j + 1], path[i][j] = n - 1;
                    dp[i][j] += data[i][j];
                }
            }
        }
// printf("dp\n");
// for(int i = 1 ; i <= n ; i++){
// for(int j = 1 ; j <= m ; j++)
// printf("%lld ", dp[i][j]);
// printf("\n");
// }
// printf("dp\n");
// printf("path\n");
// for(int i = 1 ; i <= n ; i++){
// for(int j = 1 ; j <= m ; j++)
// printf("%d ", path[i][j]);
// printf("\n");
// }
// printf("path\n");
        LL ans = dp[1][1];
        int mark = 1;
        for(int i = 2 ; i <= n ; i++){
            if(ans > dp[i][1])  ans = dp[i][1], mark = i;
        }
        int cnt = 1;
        while(cnt <= m){
// printf("cnt = %d, mark = %d\n", cnt, mark);
            out[cnt] = mark;
            mark = mark + path[mark][cnt++];
        }
        for(int i = 1 ; i < cnt ; i++){
            printf("%d", out[i]);
            if(i == cnt - 1)    printf("\n");
            else    printf(" ");
        }
        printf("%lld\n", ans);
    }
    return 0;
}
UVA 10066
裸的最长公共子序列
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 100 + 5;
int s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int main()
{
    int n, m;
    int cas = 0;
    while(scanf("%d%d", &n, &m) != EOF && n + m){
        for(int i = 1 ; i <= n ; i++)   scanf("%d", &s1[i]);
        for(int i = 1 ; i <= m ; i++)   scanf("%d", &s2[i]);
        memset(dp, 0, sizeof(dp));
        for(int i = 1 ; i <= n ; i++){
            for(int j = 1 ; j <= m ; j++){
                if(s1[i] == s2[j])  dp[i][j] = dp[i - 1][j - 1] + 1;
                else    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        printf("Twin Towers #%d\nNumber of Tiles : %d\n\n", ++cas, dp[n][m]);
    }
    return 0;
}
UVA 10131
最长上升子序列,放在结构里面按照价值先排个序,然后找最长的上升子序列就行。
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 1000 + 5;
struct D
{
    int u,v;
    int mark;
}d[MAXN];
bool cmp(D a, D b){return a.u < b.u;}
int dp[MAXN], path[MAXN];
int out[MAXN];
int main()
{
    int u, v, cnt = 0;
    while(scanf("%d%d", &u, &v) != EOF)
        d[cnt].mark = cnt + 1, d[cnt].u = u, d[cnt++].v = v;
    sort(d, d + cnt, cmp);
    for(int i = 0 ; i < cnt ; i++)  dp[i] = 1, path[i] = i;
    for(int i = 0 ; i < cnt ; i++){
        for(int j = i + 1 ; j < cnt ; j++){
            if(d[i].u < d[j].u && d[i].v > d[j].v){
                if(dp[j] < dp[i] + 1)   dp[j] = dp[i] + 1, path[j] = i;
            }
        }
    }
    int ans = dp[0];
    int mark = 0;
    for(int i = 1 ; i < cnt ; i++)
        if(ans < dp[i]) ans = dp[i], mark = i;
    cnt = 0;
    while(mark != path[mark])
        out[cnt++] = mark, mark = path[mark];
    out[cnt++] = mark;
    printf("%d\n", ans);
    for(int i = cnt - 1 ; i >= 0 ; i--){
        printf("%d", d[out[i]].mark);
// if(i) printf(" ");
        printf("\n");
    }
    return 0;
}
UVA 10192
裸的最长公共子序列。然而不明白加了答案是1时为什么不能特判输出语句为city。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
const int MAXN = 100 + 5;
char s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int main()
{
    int cas = 0;
    while(1){
        cin.getline(s1, MAXN);
        if(s1[0] == '#')    break;
        cin.getline(s2, MAXN);
        int l1 = strlen(s1), l2 = strlen(s2);
        memset(dp, 0, sizeof(dp));
        for(int i = 1 ; i <= l1 ; i++){
            for(int j = 1 ; j <= l2 ; j++){
                if(s1[i - 1] == s2[j - 1])  dp[i][j] = dp[i - 1][j - 1] + 1;
                else    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
// if(dp[l1][l2] != 1)
            printf("Case #%d: you can visit at most %d cities.\n", ++cas, dp[l1][l2]);
// else
// printf("Case #%d: you can visit at most %d city.\n", ++cas, dp[l1][l2]);
    }
    return 0;
}
UVA 147
裸的整数分化,只不过分化的种类略多。
有整数筛和记忆化+递归的写法,后者比较好写。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
#define LL long long
const int MAXN = 6000 + 5;
int data[11] = {1, 2, 4, 10, 20, 40, 100, 200, 400, 1000, 2000};
LL dp[MAXN][11];
char str[10];
LL solve(LL num, int mark)
{
    if(dp[num][mark] != -1) return dp[num][mark];
    dp[num][mark] = 0;
    for(int i = 0 ; i <= mark ; i++){
        if(num >= data[i]){
            dp[num][mark] += solve(num - data[i], i);
// if(num == 4) printf("i = %d, dp = %lld\n", i, solve(num - data[i], i));
        }
        else    break;
    }
    return dp[num][mark];
}
int main()
{
    memset(dp, -1, sizeof(dp));
    for(int i = 0 ; i < 11 ; i++){
        dp[0][i] = 1;
    }
    while(scanf("%s", str) != EOF){
        int temp = 0;
        int f = 1, cnt = 0;
        for(int i = 0 ; i < strlen(str) ; i++){
            if(str[i] == '.')   {f = 0;continue;}
            if(f == 0)  cnt++;
            temp = temp * 10 + str[i] - '0';
        }
        while(cnt < 2) cnt++, temp *= 10;
        temp /= 5;
        if(temp == 0)   break;
// printf("temp = %d\n", temp);
        LL ans = solve(temp, 10);
        printf("%6s%17lld\n", str, ans);
    }
    return 0;
}
UVA 357
一样的题……
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const int MAXN = 30000 + 5;
LL dp[MAXN][5];
int d[5] = {1, 5, 10, 25, 50};
LL solve(int num, int mark)
{
    if(dp[num][mark] != -1) return dp[num][mark];
    dp[num][mark] = 0;
    for(int i = 0 ; i <= mark ; i++){
        if(num >= d[i]) dp[num][mark] += solve(num - d[i], i);
        else    break;
    }
    return dp[num][mark];
}
int main()
{
    memset(dp, -1, sizeof(dp));
    for(int i = 0 ; i < 5 ; i++)
        dp[0][i] = 1;
    int n;
    while(scanf("%d", &n) != EOF){
        if(n == 0){
            printf("There is only 1 way to produce 0 cents change.\n");
            continue;
        }
        LL ans = solve(n, 4);
        if(ans == 1)
            printf("There is only 1 way to produce %d cents change.\n", n);
        else
            printf("There are %lld ways to produce %d cents change.\n", ans, n);
    }
    return 0;
}
UVA 10003
这道题不会写,所以写一下题解。
设dp[i][j]是以i到j为木棍切割点时最少消耗。然后就枚举一下是先切割了中间的哪一个点,然后递归处理dp[i][k]和dp[k][j]即可。其实就是简单的区间DP。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstdlib>
using namespace std;
const int MAXN = 50 + 5;
int dp[MAXN][MAXN];
int d[MAXN];
int solve(int l, int r)
{
    if(dp[l][r] != -1)  return dp[l][r];
    dp[l][r] = solve(l, l + 1) + solve(l + 1, r) + d[r] - d[l];
    for(int i = l + 2 ; i < r ; i++)
        dp[l][r] = min(dp[l][r], solve(l, i) + solve(i, r) + d[r] - d[l]);
    return dp[l][r];
}
int main()
{
    int l, n;
    while(scanf("%d", &l) != EOF && l){
        scanf("%d", &n);
        memset(dp, -1, sizeof(dp));
        d[0] = 0;
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &d[i]), dp[i - 1][i] = 0;
        }
        dp[n][n + 1] = 0;
        d[++n] = l;
        printf("The minimum cutting is %d.\n", solve(0, n));
    }
    return 0;
}
UVA 562
背包问题,看最后组成方案中最接近且小于sum/2的值是多少,转化一下即可出答案。
sum表示所有硬币值得和。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 50000 + 5;
int d[MAXN];
int dp[MAXN];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        int n;
        scanf("%d", &n);
        int sum = 0;
        for(int i = 0 ; i < n ; i++)    scanf("%d", &d[i]), sum += d[i];
        memset(dp, -1, sizeof(dp));
        dp[0] = 1;
        for(int i = 0 ; i < n ; i++){
            for(int j = sum / 2 ; j >= d[i] ; j--){
                if(dp[j - d[i]] != -1)  dp[j] = 1;
            }
        }
// for(int i = 0 ; i <= sum / 2 ; i++)
// printf("dp[%d] = %d\n", i, dp[i]);
        int ans = sum;
        for(int i = sum / 2 ; i >= 0 ; i--){
            if(dp[i] != -1){
                ans = sum - 2 * i;
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
UVA 348
石子合并问题类似,难点主要在于输出(大模拟)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 10 + 2;
int r[MAXN], c[MAXN];
LL dp[MAXN][MAXN];
int pri[MAXN];
int path[MAXN][MAXN];
void solve(int l, int r)
{
    int mark = path[l][r];
// printf("\nl = %d, r = %d, mark = %d\n", l, r, mark);
// system("pause");
    if(l == r){
        printf("A%d", l);
        return;
    }
    if(mark - l > 0)    printf("(");
    solve(l, mark);
    if(mark - l > 0)    printf(")");
    printf(" x ");
    if(r - mark - 1 > 0)   printf("(");
    solve(mark + 1, r);
    if(r - mark - 1 > 0)   printf(")");
}
int main()
{
// freopen("UVA 348.in","r", stdin);
    int n;
    int cas = 0;
    while(scanf("%d", &n) != EOF && n){
        for(int i = 1 ; i <= n ; i++)
            scanf("%d%d", &r[i], &c[i]);
        memset(dp, -1, sizeof(dp));
        for(int i = 1 ; i < n ; i++)
            dp[i][i + 1] = r[i] * c[i] * c[i + 1], dp[i][i] = 0, path[i][i + 1] = i;
        for(int k = 2 ; k <= n ; k++){
            for(int i = 1 ; i + k <= n ; i++){
                dp[i][i + k] = dp[i + 1][i + k] +  r[i] * c[i] * c[i + k];
                path[i][i + k] = i;
                for(int j = i + 1 ; j < i + k ; j++){
                    int temp = dp[i][j] + dp[j + 1][i + k] + r[i] * c[j] * c[i + k];
                    if(dp[i][i + k] > temp) path[i][i + k] = j, dp[i][i + k] = temp;
                }
            }
        }
        printf("Case %d: ", ++cas);
        printf("(");
        solve(1, n);
        printf(")");
        printf("\n");
    }
    return 0;
}
UVA 624
题目意思有点难理解,要求选取轨道的值比预计值N小且最接近N。有多种时选取用轨道最多的那种方案。
因为数据比较小而且没有给时间的上限,所以用状压DP做的。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 20 + 2;
int dp[1 << MAXN];
int d[MAXN];
int out[MAXN];
int main()
{
    int n, m;
    while(scanf("%d", &n) != EOF && n){
        scanf("%d", &m);
        for(int i = 0 ; i < m ; i++)    scanf("%d", &d[i]);
        int sum = 0;
        int cnt = 0;
        int ans = 0;
        for(int i = 1 ; i < (1 << m) ; i++){
            if(dp[i] == -1) continue;
            int tsum = 0, tans = 0;
            for(int j = 0 ; j < m ; j++){
                if(i & (1 << j))    tsum += d[j], tans++;
            }
// printf("i = %d, tsum = %d, tans = %d\n", i, tsum, tans);
            if(n - tsum >= 0 && n - tsum < n - sum)
                ans = i, cnt = tans, sum = tsum;
            else if(n - tsum == n - sum && cnt < tans)
                cnt = tans, ans = i;
        }
        cnt = 0;
        for(int i = 0 ; i < m ; i++)
            if(ans & (1 << i))  out[cnt++] = i;
        for(int i = 0 ; i < cnt ; i++)
            printf("%d ", d[out[i]]);
        printf("sum:%d\n", sum);
    }
    return 0;
}
UVA 10130
裸的01背包
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 1000 + 5;
int dp[MAXN * 30];
int w[MAXN], v[MAXN];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        int n;
        scanf("%d", &n);
        int up = 0;
        for(int i = 0 ; i < n ; i++)    scanf("%d%d", &v[i], &w[i]), up += w[i];
        memset(dp, 0, sizeof(dp));
        dp[0] = 0;
        for(int i = 0 ; i < n ; i++){
            for(int j = up ; j >= w[i] ; j--){
                dp[j] = max(dp[j], v[i] + dp[j - w[i]]);
            }
        }
        int ans = 0;
        int q;
        scanf("%d", &q);
        while(q--){
            scanf("%d", &up);
            ans += dp[up];
        }
        printf("%d\n", ans);
    }
    return 0;
}
UVA 531
最长公共子序列,用的string处理每个字符串。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 3500;
const int MAXM = 200;
string s1[MAXN], s2[MAXN];
int dp[MAXM][MAXM], path[MAXM][MAXM];
string ans[MAXN * 2];
int cnt;
void solve(int cnt1, int cnt2)
{
    if(cnt1 == 0 || cnt2 == 0)  return;
    if(path[cnt1][cnt2] == 0) solve(cnt1 - 1, cnt2 - 1);
    else if(path[cnt1][cnt2] == 1)    solve(cnt1 - 1, cnt2);
    else if(path[cnt1][cnt2] == -1)    solve(cnt1, cnt2 - 1);
    if(path[cnt1][cnt2] == 0) ans[cnt++] = s1[cnt1];
}
int main()
{
    int cnt1, cnt2;
    cnt1 = cnt2 = 0;
    while(cin>>s1[++cnt1] != NULL){
        if(s1[cnt1][0] != '#')
            while(cin>>s1[++cnt1] && s1[cnt1][0] != '#');
        while(cin>>s2[++cnt2] && s2[cnt2][0] != '#');
        cnt1--, cnt2--;
        memset(dp, 0, sizeof(dp));
        for(int i = 1 ; i <= cnt1; i ++){
            for(int j = 1 ; j <= cnt2 ; j++){
                if(s1[i] == s2[j])  dp[i][j] = dp[i - 1][j - 1] + 1, path[i][j] = 0;
                else if(dp[i][j - 1] > dp[i - 1][j])    dp[i][j] = dp[i][j - 1], path[i][j] = -1;
                else    dp[i][j] = dp[i - 1][j], path[i][j] = 1;
            }
        }
        cnt = 0;
        solve(cnt1, cnt2);
        for(int i = 0 ; i < cnt ; i++){
            cout << ans[i];
            if(i == cnt - 1)    printf("\n");
            else    printf(" ");
        }
        cnt1 = cnt2 = 0;
    }
    return 0;
}
UVA 10456
只有两个物品的完全背包
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 10000 + 5;
int dp[MAXN];
int main()
{
    int n, m, t;
    while(scanf("%d%d%d", &n, &m, &t) != EOF){
        memset(dp, -1, sizeof(dp));
        dp[0] = 0;
        for(int i = 0 ; i < MAXN ; i++){
            if(i + n > t)   break;
            if(dp[i] != -1){
                dp[i + n] = max(dp[i + n], dp[i] + 1);
            }
        }
        for(int i = 0 ; i < MAXN ; i++){
            if(i + m > t)   break;
            if(dp[i] != -1){
                dp[i + m] = max(dp[i + m], dp[i] + 1);
            }
        }
        int ans = 0;
        int beer = t;
        int ok = 0;
        int ok2 = 0;
        for(int i = t ; i >= 0 ; i--){
            if(dp[i] != -1){
                ans = dp[i];
                if(i == t)  ok = 1;
                ok2 = 1;
                beer = t - i;
                break;
            }
        }
        if(ok == 1)
            printf("%d\n", ans);
        else
            printf("%d %d\n", ans, beer);
    }
    return 0;
}
UVA 10285
记忆化+dp
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 100 + 5;
int g[MAXN][MAXN], dp[MAXN][MAXN];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
char name[MAXN];
int n, m;
int valid(int x, int y)
{
    if(x < 1 || x > n)  return false;
    if(y < 1 || y > m)  return false;
    return true;
}
int solve(int x, int y)
{
    if(dp[x][y] != -1)  return dp[x][y];
    dp[x][y] = 1;
    for(int i = 0 ; i < 4 ; i++){
        int tx = dx[i] + x;
        int ty = dy[i] + y;
        if(valid(tx, ty) && g[tx][ty] > g[x][y])
            dp[x][y] = max(dp[x][y], solve(tx, ty) + 1);
    }
    return dp[x][y];
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%s", name);
        scanf("%d%d", &n, &m);
        for(int i = 1 ; i <= n ; i++)
            for(int j = 1 ; j <= m ; j++)   scanf("%d", &g[i][j]);
        memset(dp, -1, sizeof(dp));
        int ans = 1;
        for(int i = 1 ; i <= n ; i++)
            for(int j = 1 ; j <= m ; j++)
                ans = max(ans, solve(i, j));
        printf("%s: %d\n", name, ans);
    }
    return 0;
}
UVA 437
排序后往后DP即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 30 * 6;
int dp[MAXN];
struct D
{
    int x, y, z;
    D(){}
    D(int _x, int _y, int _z){x = _x, y = _y, z = _z;}
}d[MAXN];
bool cmp(D a, D b)
{
    if(a.x != b.x)  return a.x > b.x;
    if(a.y != b.y)  return a.y > b.y;
    return a.z > b.z;
}
int main()
{
// freopen("UVA 437.in", "r", stdin);
    int n;
    int cas = 0;
    while(scanf("%d", &n) != EOF && n){
        for(int i = 0 ; i < n ; i++){
            scanf("%d%d%d", &d[i * 6].x, &d[i * 6].y, &d[i * 6].z);
            d[i * 6 + 1] = D(d[i * 6].x, d[i * 6].z, d[i * 6].y);
            d[i * 6 + 2] = D(d[i * 6].y, d[i * 6].x, d[i * 6].z);
            d[i * 6 + 3] = D(d[i * 6].y, d[i * 6].z, d[i * 6].x);
            d[i * 6 + 4] = D(d[i * 6].z, d[i * 6].x, d[i * 6].y);
            d[i * 6 + 5] = D(d[i * 6].z, d[i * 6].y, d[i * 6].x);
        }
        n *= 6;
        sort(d, d + n, cmp);
        for(int i = 0 ; i < n ; i++)    dp[i] = 0;
        int ans = 0;
        for(int i = 0 ; i < n ; i++){
            dp[i] += d[i].z;
            ans = max(ans, dp[i]);
            for(int j = i + 1 ; j < n ; j++){
                if(d[i].x > d[j].x && d[i].y > d[j].y)
                    dp[j] = max(dp[i], dp[j]);
            }
        }
// printf("/////*****n = %d***////\n", n);
// for(int i = 0 ; i < n ; i++)
// printf("x = %d, y = %d, z = %d, dp[%d] = %d\n", d[i].x, d[i].y, d[i].z, i, dp[i]);
        printf("Case %d: maximum height = %d\n", ++cas, ans);
    }
    return 0;
}
UVA 825
题面好奇怪。
就是一个地图有坏点不能走,问从(11)走到(n,m)的方案数。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 1000 + 5;
long long dp[MAXN][MAXN];
int g[MAXN][MAXN];
char s[MAXN];
int main()
{
    int t;
    scanf("%d", &t);
    int f = 1;
    while(t--){
        int n, m;
        scanf("%d%d", &n, &m);
        memset(dp, 0, sizeof(dp));
        memset(g, 0, sizeof(g));
        for(int i = 1; i <= n ; i++){
            int u;
            scanf("%d", &u);
            cin.getline(s, MAXN);
            int temp = 0;
            for(int j = 0 ; j < (int)strlen(s) ; j++){
                if(s[j] == ' '){
                    g[u][temp] = -1;
                    temp = 0;
                }
                else    temp = temp * 10 + s[j] - '0';
            }
            if(temp != 0)   g[u][temp] = -1;
        }
        if(f)   f = 0;
        else    printf("\n");
        if(g[1][1] == -1 || g[n][m] == -1){
            printf("0\n");
            continue;
        }
        dp[1][1] = 1;
        for(int i = 1 ; i <= n ; i++){
            for(int j = 1 ; j <= m ; j++){
                if(g[i][j] == -1)   continue;
                dp[i][j + 1] += dp[i][j];
                dp[i + 1][j] += dp[i][j];
            }
        }
        printf("%lld\n", dp[n][m]);
    }
    return 0;
}
UVA 10069
dp[i]表示表示在字符串Z中到i结尾的种类多少个。
按照顺序遍历S字符串,按照倒序遍历Z字符串,就把问题转化成一个类似于01背包的问题。
作死用java写的,主要是字符串处理。String.AtChar(int pos)可以访问第i位的字符,cin.NextLine()可以完成输出。
交文件的时候把package注释掉,第一个public class后面接Main
//package uva10069;
import java.io.*;
import java.math.*;
import java.text.*;
import java.util.*;
//import java.io.BufferedReader; 
//import java.io.IOException; 
//import java.io.InputStreamReader
public class Main {
        public static void main(String[] args)
        {
            Scanner cin = new Scanner(new BufferedInputStream(System.in));
// BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in)); 
            int t;
            t = cin.nextInt();
            String s1 = cin.nextLine();
            while(t > 0){
                t--;
                s1 = cin.nextLine();
                String s2 = cin.nextLine();
                int l1 = s1.length();
                int l2 = s2.length();
                if(l2 == 0){
                    System.out.println("1\n");
                    continue;
                }
// char[] s1 = t1.charAt(0);
// char[] s2 = t2.charAt(0);
                BigInteger[] dp;
                dp = new BigInteger[1000];
                BigInteger one = BigInteger.valueOf(1);
                for(int i = 0 ; i < l2 ; i++)
                    dp[i] = BigInteger.valueOf(0);
// System.out.println("t1 = " + t1 + "\nt2 = " + t2 + "\n");
// System.out.println("s1 = " + s1 + "\ns2 = " + s2 + "\n");
                for(int i = 0 ; i < l1 ; i++){
                    for(int j = l2 - 1 ; j >= 0 ; j--){
                        if(s1.charAt(i) == s2.charAt(j)){
// System.out.println("i = " + i + " j = " + j + "\n");
                            if(j == 0)  dp[j] = dp[j].add(one);
                            else    dp[j] = dp[j].add(dp[j - 1]);
                        }
                    }
                }
// for(int i = 0 ; i < l1 ; i++){
// System.out.println("s1.Atchar[" + i + "] = " + s1.charAt(i) + "\n");
// }
// for(int i = 0 ; i < l2 ; i++)
// System.out.println("dp[" + i + "] = " + dp[l2 - 1] + "\n");
                System.out.println(dp[l2 - 1]);
// System.out.println("\n");
            }
        }
}
UVA 10534
最长上升子序列的变形。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 10000 + 5;
int dpl[MAXN], dpr[MAXN];
int d[MAXN];
int l[MAXN], r[MAXN];
int main()
{
    int n;
    while(scanf("%d", &n) != EOF){
        for(int i = 0 ; i < n ; i++)    scanf("%d", &d[i]);
        int cnt = 0;
        l[0] = 0;
        for(int i = 0 ; i < n ; i++){
            if(d[i] > l[cnt]){
                l[++cnt] = d[i];
                dpl[i] = cnt;
            }
            else{
                int mark = lower_bound(l, l + cnt, d[i]) - l;
                l[mark] = min(l[mark], d[i]);
                dpl[i] = mark;
            }
        }
        cnt = 0;
        for(int i = n - 1 ; i >= 0 ; i--){
            if(d[i] > l[cnt]){
                l[++cnt] = d[i];
                dpr[i] = cnt;
            }
            else{
                int mark = lower_bound(l, l + cnt, d[i]) - l;
                l[mark] = min(l[mark], d[i]);
                dpr[i] = mark;
            }
        }
// printf("dpl\n");
// for(int i = 0 ; i < n ; i++)
// printf("%d ", dpl[i]);
// printf("dpl\n");
// printf("dpr\n");
// for(int i = 0 ; i < n ; i++)
// printf("%d ", dpr[i]);
// printf("dpr\n");
        int ans = 0;
        for(int i = 0 ; i < n ; i++)
            ans = max(ans, min(dpl[i], dpr[i]) * 2 - 1);
        printf("%d\n", ans);
    }
    return 0;
}
UVA 10051
搞清楚状态转移就行,比如用这个重量的front的dp值去更新重量更大的方块的back的dp值,具体用异或处理一下就可以。数组开小WA了几次。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const int MAXN = 3000 + 5;
int dp[MAXN * 6];
int path[MAXN * 6];
int d[MAXN][6];
vector<int>lin[105];
int x[MAXN], y[MAXN];
int main()
{
    int n;
    int cas = 0;
    while(scanf("%d", &n) != EOF && n){
        for(int i = 0 ; i <= 100 ; i++)
            lin[i].clear();
        for(int i = 0 ; i < n ; i++){
            for(int j = 0 ; j < 6 ; j++){
                scanf("%d", &d[i][j]), dp[i * 6 + j] = 1;
                path[i * 6 + j] = i * 6 + j;
                lin[d[i][j]].push_back(i * 6 + j);
            }
        }
        int ans = 0;
        int mark = 0;
        for(int i = 0 ; i < n ; i++){
            for(int j = 0 ; j < 6 ; j++){
                int now = (i * 6 + (j ^ 1));
                int k = lower_bound(lin[d[i][j]].begin(), lin[d[i][j]].end(), now) - lin[d[i][j]].begin();
                for(; k < (int)lin[d[i][j]].size() ; k++){
                    int ne = lin[d[i][j]][k];
                    if(ne / 6 <= now / 6)   continue;
// if(i == 0 && j == 0){
// printf("now = %d, ne = %d\n", now, ne);
// }
                    if(dp[ne] < dp[now] + 1){
                        dp[ne] = dp[now] + 1;
                        path[ne] = now;
                    }
                }
                if(dp[now] > ans){
                    ans = dp[now], mark = now;
                }
            }
        }
        int cnt = 0;
        while(1){
            x[cnt] = mark / 6, y[cnt++] = mark % 6;
            if(path[mark] == mark)  break;
            mark = path[mark];
        }
        if(cas != 0)    printf("\n");
        printf("Case #%d\n", ++cas);
        printf("%d\n", ans);
        for(int i = cnt - 1 ; i >= 0 ; i--){
            printf("%d ", x[i] + 1);
            if(y[i] == 0) printf("front\n");
            else if(y[i] == 1)  printf("back\n");
            else if(y[i] == 2)  printf("left\n");
            else if(y[i] == 3)  printf("right\n");
            else if(y[i] == 4)  printf("top\n");
            else if(y[i] == 5)  printf("bottom\n");
        }
// printf("\n");
    }
    return 0;
}
/* 3 1 2 2 2 1 2 3 3 3 3 3 3 3 2 1 1 1 1 10 1 5 10 3 6 5 2 6 7 3 6 9 5 7 3 2 1 9 1 3 3 5 8 10 6 6 2 2 4 4 1 2 3 4 5 6 10 9 8 7 6 5 6 1 2 3 4 7 1 2 3 3 2 1 3 2 1 1 2 3 0 */
UVA 10651
用状压DP的方式模拟+记忆化就可以。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
const int MAXN = 13;
int dp[1<<MAXN];
char str[20];
int solve(int s)
{
    if(dp[s] != -1) return dp[s];
    int val = 0;
    for(int i = 0 ; i < 12 ; i++)   if((1 << i) & s)    val++;
    dp[s] = val;
    for(int i = 0 ; i < 12 ; i++){
        if((s & (1 << i))){
            if(i > 1 && (s & (1 << (i - 1))) && !(s & (1 << (i - 2)))){
                int ts = (s ^ (1 << (i - 2)) ^ (1 << (i - 1)) ^ (1 << (i)));
                dp[s] = min(dp[s], solve(ts));
            }
            if(i < 10 && (s & (1 << (i + 1))) && !(s & (1 << (i + 2)))){
                int ts = (s ^ (1 << (i + 2)) ^ (1 << (i + 1)) ^ (1 << (i)));
                dp[s] = min(dp[s], solve(ts));
            }
        }
    }
    return dp[s];
}
int main()
{
    memset(dp, -1, sizeof(dp));
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%s", str);
        int ts = 0;
        for(int i = 11 ; i >= 0 ; i--){
            if(str[i] == 'o') ts += (1 << i);
        }
        printf("%d\n", solve(ts));
    }
    return 0;
}


你可能感兴趣的:(小白书部分动规习题)