动态规划总专题

 

POJ 1458

    LCS。

View Code
#include <stdio.h>
#include <string.h>
#define N 1001
char a[N], b[N];
int dp[N][N];

int max(int x, int y){return x > y ? x : y;}

int solve()
{
    int n = strlen(a), m = strlen(b);
    int i, j;
    for(i = 0; i <= n; i ++)
        dp[i][0] = 0;
    for(j = 0; j <= m; j ++)
        dp[0][j] = 0;
    for(i = 1; i <= n; i ++)
        for(j = 1; j <= m; j ++)
            if(a[i - 1] == b[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    return dp[n][m];
}

int main()
{
    while(scanf("%s%s", a, b) != EOF)
        printf("%d\n", solve());
    return 0;
}

 

POJ 2250

    LCS,只不过字母比较换成了字符串比较,输出要花点时间,算法导论上有很详细的讲解。

View Code
#include<iostream>
#include<string>
#include<vector>
using namespace std;
vector<string> a,b;
int c[101][101];
short d[101][101];
int lena,lenb;

void run()
{
    lena = a.size();
    lenb = b.size();
    for(int i=0;i<101;i++)
        c[i][0] = c[0][i] = 0;
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
            if(a[i-1]==b[j-1])
            {
                c[i][j]=c[i-1][j-1]+1;
                d[i-1][j-1]=0;
            }
            else if(c[i-1][j]>c[i][j-1])
            {
                c[i][j]=c[i-1][j];
                d[i-1][j-1]=1;
            }
            else
            {
                c[i][j]=c[i][j-1];
                d[i-1][j-1]= -1;
            }
        }
    }
}

void print_lcs(int x,int y)
{
    if(x<0||y<0)return;
    if(d[x][y]==0)
    {
        print_lcs(x-1,y-1);
        if(x==0||y==0)
            cout<<a[x];
        else cout<<" "<<a[x];
    }
    else if(d[x][y]==1)
        print_lcs(x-1,y);
    else print_lcs(x,y-1);
}


int main()
{
    string tmp;
    while(cin >> tmp)
    {
        a.clear();
        b.clear();
        do
        {
            a.push_back(tmp);
        }while(cin >> tmp&&tmp!="#");
        while(cin>>tmp&&tmp!="#")
        {
            b.push_back(tmp);
        }
        run();
        print_lcs(lena-1,lenb-1);
        cout<<endl;
    }
    return 0;
}

 

POJ 1159

    将字符串反转后和原字符串做LCS就差不多了。

View Code
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 5001;
short c[maxn][maxn];
int n;
string a,b;

void lcs()
{
    for(int i=0;i<n;i++)
        c[i][0]=c[0][i]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(a[i-1]==b[j-1])
                c[i][j]=c[i-1][j-1]+1;
            else if(c[i-1][j]>c[i][j-1])
                c[i][j] = c[i-1][j];
            else c[i][j] = c[i][j-1];
        }
    }
}
int main()
{
    while(cin >> n)
    {
        cin >> a;
        b = a;
        reverse(b.begin(),b.end());
        lcs();
        cout<<n-c[n][n]<<endl;
    }
    return 0;
}

 

POJ 1887

    最长下降子序列。

    转移方程:dp[i] = max(dp[i], dp[j] + 1)(j = 0...i-1 && v[i] <= v[j]),时间复杂度O(n^2)。

View Code
#include <cstdio>
#include <cstring>
int n, a[100001], dp[100001];
inline int max(int x, int y)
{
    return x > y ? x : y;
}

int solve()
{
    int i, j, m = 1;
    dp[0] = 1;
    for(i = 1; i < n; i ++)
    {
        dp[i] = 1;
        for(j = 0; j < i; j ++)
        {
            if(a[i] <= a[j])
                dp[i] = max(dp[i], dp[j] + 1);
        }
        if(dp[i] > m)
            m = dp[i];
    }
    return m;
}

int main()
{
    int v, cas = 1;
    while(scanf("%d", &v), v != -1)
    {
        n = 0;
        do
        {
            a[n ++] = v;
        }while(scanf("%d", &v), v != -1);
        if(cas != 1)
            printf("\n");
        printf("Test #%d:\n", cas ++);
        printf("  maximum possible interceptions: %d\n", solve());
    }
    return 0;
}

    用“贪心 + 二分”可以达到nlogn,维护dp为递减序列,不断更新dp序列的某个位置为在该位置可能出现的最大值,如果要插入的v值小于该递减序列最小值,则将v插入到队尾。

View Code
#include <cstdio>
#define N 50001
int n, m, dp[N];

int bs(int v)
{
    int l = 0, r = m;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if(dp[mid] >= v)
            l = mid + 1;
        else
            r = mid;
    }
    return l;
}

int main()
{
    int v, cas = 1;
    while(scanf("%d", &v), v != -1)
    {
        m = 0;
        do
        {
            if(m == 0)
                dp[m ++] = v;
            else
            {
                int k = bs(v);
                if(k == m)
                    dp[m ++] = v;
                else   
                    dp[k] = v;
            }
        }while(scanf("%d", &v), v != -1);
        if(cas != 1) 
            putchar('\n');
        printf("Test #%d:\n", cas ++);
        printf("  maximum possible interceptions: %d\n", m);
    }
    return 0;
}

 

POJ 1631

    看出是LIS差不多已经赢了,而此题基数大必须nlogn,把上面POJ1887题的nlogn代码复制过来稍微改改就行了。

View Code
#include <cstdio>
#define N 40001
int m, q[N];

int bs(int v)
{
    int l = 0, r = m;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if(q[mid] < v)
            l = mid + 1;
        else
            r = mid;
    }
    return r;
}

int main()
{
    int n, p, v;
    scanf("%d", &n);
    while(n --)
    {
        scanf("%d", &p);
        m = 0;
        while(p --)
        {
            scanf("%d", &v);
            if(m == 0)
                q[m ++] = v;
            else
            {
                int k = bs(v);
                if(k == m)
                    q[m ++] = v;
                else
                    q[k] = v;
            }
        }
        printf("%d\n", m);
    }
    return 0;
}

 

POJ 3624

    没什么好讲的,01背包。

View Code
#include <cstdio>
#include <cstring>
#define MAXN 13000
int W[MAXN], D[MAXN], dp[MAXN];
int N, M;

int max(int a, int b)
{
    return a > b ? a : b;
}

int CharmBracelet()
{
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < N; i ++)
    {
        for(int j = M; j >= W[i]; j --)
        {
            dp[j] = max(dp[j], dp[j - W[i]] + D[i]);
        }
    }
    return dp[M];
}

int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        for(int i = 0; i < N; i ++)
        {
            scanf("%d%d", &W[i], &D[i]);
        }
        printf("%d\n", CharmBracelet());
    }
    return 0;
}

 

POJ 2192

    看str1的前i个字母和str2的前j个字母能否组成str的前i+j个字母。

View Code
#include <stdio.h>
#include <string.h>
#define maxn 205
char a[maxn], b[maxn], c[maxn << 1];
bool dp[maxn][maxn];

bool solve()
{
    int i, j, n = strlen(a), m = strlen(b);
    for(i = 0; i < n; i ++)
        if(a[i] == c[i])
            dp[i + 1][0] = true;
    for(i = 0; i < m; i ++)
        if(b[i] == c[i])
            dp[0][i + 1] = true;
    dp[0][0] = true;
    for(i = 1; i <= n; i ++)
    {
        for(j = 1; j <= m; j ++)
        {
            if((dp[i - 1][j] && a[i - 1] == c[i + j - 1]) || (dp[i][j - 1] && b[j - 1] == c[i + j - 1]))
                dp[i][j] = true;
            else
                dp[i][j] = false;
        }
    }
    return dp[n][m];
}

int main()
{
    int n, i;
    scanf("%d", &n);
    for(i = 1; i <= n; i ++)
    {
        scanf("%s%s%s", a, b, c);
        if(solve())
            printf("Data set %d: yes\n", i);
        else
            printf("Data set %d: no\n", i);
    }
    return 0;
}

 

ZOJ 3631

     这题由于基数M很大,无法用传统意义上的背包,但总体思想还是背包,实现可以用dfs+剪枝。

View Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 10000001

int a[31], ans;
int n, m;

int max(int x, int y){return x > y ? x : y;}

bool cmp(int x, int y){return x > y;}

void dfs(int d, int s)
{
    if(ans == m) return;
    if(s > m) return ;
    if(d >= n)
    {
        ans = max(ans, s);
        return ;
    }
    int sum = s;
    for(int i = d; i < n; i ++)  // 剪枝
        sum += a[i];
    if(sum < ans)
        return ;
    dfs(d + 1, s + a[d]);
    dfs(d + 1, s);
}

int main()
{
    int i;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        int sum = 0;
        for(i = 0; i < n; i ++)
        {
            scanf("%d", &a[i]);
            sum += a[i];
        }
        if(sum <= m)
        {
            printf("%d\n", sum);
            continue;
        }
        ans = 0;
        std::sort(a, a + n, cmp);
        dfs(0, 0);
        printf("%d\n", ans);
    }
    return 0;
}

 

HDU 1059

     多重背包。

View Code
#include <stdio.h>
#include <string.h>
#define N 60010

int f[N], V;

inline int max(int x, int y){return x > y ? x : y;}

void ZeroOnePack(int c, int w)
{
    int i;
    for(i = V; i >= c; i --)
        f[i] = max(f[i], f[i - c] + w);
}

void CompletePack(int c, int w)
{
    int i;
    for(i = c; i <= V; i ++)
        f[i] = max(f[i], f[i - c] + w);
}

void MultiplePack(int c, int w, int n)
{
    if(c * n >= V)
    {
        CompletePack(c, w);
        return ;
    }
    int k = 1;
    while(k <= n)
    {
        ZeroOnePack(k * c, k * w);
        n -= k;
        k *= 23;
    }
    ZeroOnePack(n * c, n * w);
}

int main()
{
    int i, sum, n[6], cas = 1;
    while(1)
    {
        sum = 0;
        for(i = 0; i < 6; i ++)
        {
            scanf("%d", &n[i]);
            sum += (i + 1) * n[i];
        }

        if(sum == 0)
            break;

        printf("Collection #%d:\n", cas ++);

        if(sum & 1)
        {
            printf("Can't be divided.\n\n");
            continue;
        }

        V = sum / 2;
        memset(f, 0, sizeof(f));
        for(i = 0; i < 6; i ++)
            if(n[i]) MultiplePack(i + 1, i + 1, n[i]);

        if(f[V] == V)
            printf("Can be divided.\n\n");
        else
            printf("Can't be divided.\n\n");
    }
    return 0;
}

 

HDU 2955 Robberies

  一开始傻逼了,居然直接把概率进行相加,用转移方程f[i] = max(f[i], f[i-v[j].money] + v[j].p],这是错的。

  正确解法是:设f[i]为得到总钱数为 i 的最大逃脱概率,那么有转移方程 f[i] = max(f[i], f[i-v[j].money] * v[j].p),逃脱概率v[j].p = 1.- 被抓捕概率。那么最后结果就是大于题目给定最大逃脱概率前提下的最多钱数。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #define N 10001
 5 
 6 double f[N];
 7 
 8 int main()
 9 {
10     int T, n, mm;
11     double p, pp;
12     scanf("%d", &T);
13     while(T--)
14     {
15         scanf("%lf%d", &p, &n);
16         p = 1 - p;
17         for(int i = 0; i < N; i++)
18             f[i] = 0.0;
19         f[0] =  1.0;
20         for(int i = 0; i < n; i++)
21         {
22             scanf("%d%lf", &mm, &pp);
23             pp = 1.0 - pp;
24             for(int j = N-1; j >= mm; j--)
25                 f[j] = std::max(f[j], f[j - mm] * pp);
26         }
27         int i;
28         for(i = N-1; i >= 0; i--)
29             if(f[i] >= p)
30                 break;
31         printf("%d\n", i);
32     }
33     return 0;
34 }
View Code

 

    努力更新中……

 

posted @ 2013-10-04 13:11  芒果布丁  阅读(240)  评论(0编辑  收藏  举报