zoukankan      html  css  js  c++  java
  • DP专题

    最近做了一些动态规划的题目,稍微回顾一下

    hdu2517 棋盘分割

    题意:中文题……

    分析:这个题目在黑书上面有,主要是将公式稍微转换一下

    hdu2517
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<math.h>
    
    using namespace std;
    
    const int N = 10;
    const int M = 20;
    const int n = 8;
    double dp[M][N][N][N][N], ss[N][N][N][N];
    
    int a[N][N], s[N][N];
    
    double sum(int x1, int y1, int x2, int y2)
    {
        if(ss[x1][y1][x2][y2] >= 0)
            return ss[x1][y1][x2][y2];
        int tmp = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
        return ss[x1][y1][x2][y2] = tmp * tmp;
    }
    double DP(int k, int x1, int y1, int x2, int y2)
    {
        if(k == 1)
        {
            double tmp = sum(x1, y1, x2, y2);
            return dp[k][x1][y1][x2][y2] = tmp;
        }
        double tmp = dp[k][x1][y1][x2][y2];
        if(tmp >= 0)
            return tmp;
        tmp = 99999999999;
        for(int i = x1; i < x2; ++i)
        {
            tmp = min(tmp , DP(k - 1, x1, y1, i, y2) + sum(i + 1, y1, x2, y2));
            tmp = min(tmp , DP(k - 1, i + 1, y1, x2, y2) + sum(x1, y1, i , y2));
        }
        for(int j = y1; j < y2; ++j)
        {
            tmp = min(tmp , DP(k - 1, x1, y1, x2, j) + sum(x1, j + 1, x2, y2));
            tmp = min(tmp , DP(k - 1, x1, j + 1, x2, y2) + sum( x1 , y1, x2, j));
        }
        return dp[k][x1][y1][x2][y2] = tmp;
    }
    int main()
    {
        int m;
        while(scanf("%d",&m) == 1)
        {
            memset(s, 0 ,sizeof(s));
            int tot = 0;
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= n; ++j)
                {
                    scanf("%d",&a[i][j]);
                    tot += a[i][j];
                    s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j -1 ];
                }
            memset(dp,-1,sizeof(dp));
            memset(ss,-1,sizeof(ss));
            double ans = DP(m, 1, 1, n, n);
            double ave = s[n][n] * 1.0 / m;
            ave *= ave;
            printf("%.3f\n",sqrt(ans * 1.0 / m - ave));
        }
        return 0;
    }
    
            

    pku1015 Jury Compromise

    此题用dp解决相当灵活,这个博客解释的很不错 http://blog.chinaunix.net/uid-20494489-id-1940446.html

    pku1015
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    
    using namespace std;
    
    const int N = 850;
    
    int dp[25][N], path[25][N];
    int sub[N], plus[N], ans[25];
    
    int main()
    {
        int a, b, n, m, cas = 0;
        while(scanf("%d %d",&n,&m) == 2 && (n || m))
        {
            for(int i = 1; i <= n; ++i)
            {
                scanf("%d %d",&a,&b);
                sub[i] = a - b;
                plus[i] = a + b;
            }
            memset(dp,-1,sizeof(dp));
            memset(path,-1,sizeof(path));
            int low = 21 * m;
            dp[0][low] = path[0][low] = 0;
    
            for(int i = 1; i <= n; ++i) //先计算选择一个人的情况
            {
                if(dp[1][low + sub[i]] < plus[i])
                {
                    path[1][low + sub[i]] = i;
                    dp[1][low + sub[i]] = plus[i];
                }
            }
    
            for(int i = 1; i < m; ++i)
                for(int j = 0; j < 2 * low; ++j)
                {
                    if(path[i][j] >= 0)
                    {
                        for(int k = 1; k <= n; ++k)
                        {
                            if(dp[i + 1][ j + sub[k] ] < dp[i][j] + plus[k])
                            {
                                int ii = i, jj = j;
                                for(; ii >= 1; --ii) //判断k是否已经在dp[i][j]中
                                {
                                    if(path[ii][jj] == k) break;
                                    jj -= sub[ path[ii][jj] ];
                                }
    
                                if( ii < 1 )
                                {
                                    dp[i + 1][ j + sub[k] ] = dp[i][j] + plus[k];
                                    path[i + 1][ j + sub[k] ] = k;
                                }
                            }
                        }
                    }
                }
            int tmp;
            for(int i = 0; i < low; ++i)
            {
                if( dp[m][ low + i ] >= 0 || dp[m][ low - i ] >= 0)
                {
                    if(dp[m][ low + i ] > dp[m][ low - i ])
                        tmp = low + i;
                    else tmp = low - i;
                    break;
                }
            }
            printf("Jury #%d\n",++cas);
            printf("Best jury has value %d for prosecution and value %d for defence:\n",(dp[m][tmp] + tmp - low)/2,(dp[m][tmp] - tmp + low)/2);
            int num = 0;
            for(int j = m, k = tmp; j >= 1; --j)
            {
                ans[num++] = path[j][k];
                k -= sub[ ans[num - 1] ];
            }
            sort(ans, ans + num);
            for(int i = 0; i < num; ++i)
                printf(" %d",ans[i]);
            puts("");
        }
        return 0;
    }

    pku1160 Post Office

    题意:给出m个村庄及其距离,给出n个邮局,要求怎么建n个邮局使代价最小。

    分析:

    先预处理,把第i个村子到第j个村子中,建一个邮局的最小代价算出来,存在cost[i][j]里。
    接下来就可以DP。设dp[i][j]为前i个邮局,建在前j个村子的最小代价。
    那么dp[i][j]可以转移到dp[i + 1][j + k],(1 <= k 且 j + k <= n),代价是cost[j + 1][j + k]。

    pku1160
    #include<iostream>
    #include<algorithm>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<math.h>
    
    using namespace std;
    
    const int N = 300 + 10;
    
    int cost[N][N], dp[35][N];
    int seq[N];
    
    int main()
    {
        int n, m;
        while(scanf("%d %d",&n,&m) == 2)
        {
            for(int i = 1; i <= n; ++i)
                scanf("%d",&seq[i]);
            for(int i = 1; i <= n; ++i)
            {
                for(int j = i; j <= n; ++j)
                {
                    cost[i][j] = 0;
                    int mid = (i + j) >> 1;
                    for(int k = i; k <= mid; ++k)
                        cost[i][j] += seq[mid] - seq[k];
                    for(int k = mid + 1; k <= j; ++k)
                        cost[i][j] += seq[k] - seq[mid];
                }
            }
            for(int i = 0;i <= m; ++i)
                for(int j = 0; j <= n; ++j)
                    dp[i][j] = INT_MAX;
            dp[0][0] = 0;
            for(int i = 0; i < m; ++i)
                for(int j = 0; j < n; ++j)
                    if(dp[i][j] < INT_MAX)
                    {
                        for(int k = 1; j + k <= n; ++k)
                            dp[i + 1][j + k] = min(dp[i + 1][j + k], dp[i][j] + cost[j + 1][j + k]);
                    }
            printf("%d\n",dp[m][n]);
        }
        return 0;
    }

    pku1179 Polygon

    多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。
    游戏第1步,将一条边删除。
    随后n-1步按以下方式操作:
    (1)选择一条边E以及由E连接着的2个顶点V1和V2;
    (2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点。
    最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值。
    问题:对于给定的多边形,计算最高得分。

    分析:记忆化搜索

    dp1[i][j] 表示以i 开始长度为j 的链的最大值,
    dp2[i][j] 为最小值

    假设当前要求的是dp1[i][j],通过枚举这条长度为J 的直链断开的地方,切成俩段,再迭代求子链

    注意存在负值,所以做乘法运算的时候需要用到左右子链的最小值,假设左右自链的最大最小值分别为a, b, c, d, 所以dp1[i][j] = max(a*c, max(a*d, max(b*c, b*d)));

    pku1179
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 50 + 10;
    
    int dp1[N][N], dp2[N][N], n;
    //dp1[i][j] 表示以i 开始长度为j 的链的最大值,
    //dp2[i][j] 为最小值
    int v[N];
    char op[N];
    
    void DP(int s, int len)
    {
        if(len == 0)
        {
            dp1[s][len] = dp2[s][len] = v[s];
            return ;
        }
    
        if(dp1[s][len] != INT_MIN)
            return ;
    
        for(int i = s; i < s + len;  ++i)
        {
            int s1 = i + 1 > n ? (i + 1) - n : i + 1;
            int len1 = i - s, len2 = len - 1 - (i - s);
            DP(s, len1);
            DP(s1, len2);
            int a = dp1[s][len1], b = dp2[s][len1];
            int c = dp1[s1][len2], d = dp2[s1][len2];
            int in = i > n? i - n : i ;
    
            if(op[in] == '+')
            {
                dp1[s][len] = max(dp1[s][len], a + c);
                dp2[s][len] = min(dp2[s][len], b + d);
            }
            else {
                dp1[s][len] = max(dp1[s][len], max(a * c, max(a * d, max(b * c, b * d))));
                dp2[s][len] = min(dp2[s][len], min(a * c, min(a * d, min(b * c, b * d))));
            }
        }
    
        return ;
    }
    
    int main()
    {
        while(scanf("%d",&n) == 1)
        {
            char str[5];
    
            for(int i = 1; i <= n; ++i)
            {
                scanf("%s %d",str,&v[i]);
                op[i] = str[0] == 't' ? '+' : '*';
            }
            int tmp = op[1];
            for(int i = 2; i <= n; ++i)
                op[i - 1] = op[i];
            op[n] = tmp;
    
            for(int i = 0; i <= n; ++i)
                for(int j = 0; j <= n; ++j)
                    dp1[i][j] = INT_MIN, dp2[i][j] = INT_MAX;
            int ans = INT_MIN;
            for(int i = 1; i <= n; ++i)
            {
                DP(i, n - 1);
                ans = max ( ans, dp1[i][n - 1] );
            }
            printf("%d\n",ans);
    
            bool flag = true;
            for(int i = 1; i <= n; ++i)
            {
                if( dp1[i][n - 1] == ans )
                {
                    if(flag)
                    {
                        printf("%d",i);
                        flag = false;
                    }
                    else printf(" %d",i);
                }
    
            }
            puts("");
        }
        return 0;
    }

    pku1189 钉子和小球

    题意:中文题目……

    拔掉某些钉子后,小球落在编号为m的格子中的概率pm。
    那么计算过程中记录某个落到某一个各自的概率,中间涉及到分数的问题。当时,如果注意到一点,所有概率值的分母都是2^k,所以,初始概率dp[0][0] 的值初始化为2n 而不是1,这样就避免了分数的运算,最后再将结果处理一下即可。

    pku1189
    #include <stdio.h>
     #include <string.h>
     
     long long dp[56][56];
     
     long long gcd(long long x, long long y)
     {
         if( y == 0 ) 
             return x;
         return gcd(y, x % y);
     }
     
     
     int main()
     {
         int n, m;
         char s[2];
         while(scanf("%d %d",&n,&m) == 2)
         {
             memset(dp,0,sizeof(dp));
             dp[1][1] = (long long)1 << 56;
             for(int i = 1; i <= n; ++i)
             {
                 for(int j = 1; j <= i; ++j)
                 {
                     scanf("%s",s);
                     if(s[0] == '*')
                     {
                         dp[i + 1][j] += (dp[i][j] >> 1);
                         dp[i + 1][j + 1] += (dp[i][j] >> 1);
                     }
                     else dp[i + 2][j + 1] += dp[i][j];
                 }
             }
             long long r = dp[n + 1][m + 1];
             if( r == 0)
             {
                 printf("0/1\n");
                 continue;
             }
             long long h = gcd(r, dp[1][1]);
             if( h == 1 )
             {
                 printf("%lld\n",r);
                 continue;
             }
             printf("%lld/%lld\n",(long long) r/h,dp[1][1]/h);
         }
         return 0;
     }

    pku1699 Best Sequence

    题意:给定N 个字符串,求能包含这N 个字符串的最短长度序列。

    分析:N 最大为10,所以可以使用状态压缩+ spfa

    首先预处理一下数组g[i][j],表示以第j个字符串接在第i个字符串后面的第一个匹配位置。

    dp[v][state]表示以第v的字符串结尾,状态为state时的最短长度,state的每一个2进制位为1表示已经包含了某一个字符串

    pku1699
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<vector>
    #include<queue>
    
    using namespace std;
    
    const int N = 10 + 5;
    const int M = 20 + 10;
    
    char str[N][M];
    int g[N][N], len[N];
    int n;
    int dp[N][1 << N];
    bool vis[N][1 << N];
    
    struct node
    {
        int v,state;
        node(){}
        node(int v,int state):v(v),state(state){}
    };
    
    void spfa()
    {
        memset(vis,false,sizeof(vis));
        memset(dp,-1,sizeof(dp));
        dp[0][0]=0;
        queue<node> Q;
        Q.push(node(0,0));
        vis[0][0]=true;
        while(!Q.empty())
        {
            node tmp=Q.front();
            Q.pop();
            vis[tmp.v][tmp.state]=false;
    
            for(int i = 1; i <= n; ++i)
            {
                int state = tmp.state | (1 << (i - 1));
    
                if(tmp.v == i || (tmp.state & ( 1 << (i - 1) ))) continue;
    
                if(tmp.v == 0)
                {
                    dp[i][state] = len[i];
                    Q.push(node(i, state));
                    vis[i][state] = true;
                    continue;
                }
    
                int u = tmp.v, v = i, L = len[v] - (len[u] - g[u][v]);
    
                if(L >= 0 && (dp[v][state] == -1 || dp[v][state] > dp[u][tmp.state] + L))
                {
                    dp[v][state] = dp[u][tmp.state] + L;
                    if(!vis[v][state])
                    {
                        vis[v][state]=true;
                        Q.push(node(v,state));
                    }
                }
                if(L <= 0)
                {
                    v = u;
                    L = 0;
                    if(dp[v][state] == -1 || dp[v][state] > dp[u][tmp.state] + L)
                    {
                        dp[v][state] = dp[u][tmp.state];
                        if(!vis[v][state]) {
                            vis[v][state] = true;
                            Q.push(node(v,state));
                        }
                    }
                }
            }
        }
    }
    int match(int a, int b)
    {
        int res = 0;
        for(; res < len[a]; ++res)
        {
            int i = res, j = 0;
            while(i < len[a] && j < len[b])
            {
                if(str[a][i] != str[b][j])
                    break;
                ++i;
                ++j;
            }
            if(j == len[b] || i == len[a] ) break; 
        }
        return res;
    }
    void init()
    {
        
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= n; ++j)
            {
                if( i == j ) 
                    g[i][j] = 0;
                else
                g[i][j] = match(i, j);
            }
            g[0][i] = len[i];
        }
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i = 1; i <= n; ++i)
            {
                scanf("%s",str[i]);
                len[i] = strlen(str[i]);
            }
            init();
            //for(int i = 1; i <= n; ++i)
            //{
            //    for(int j = 1; j <= n; ++j)
            //        cout << g[i][j] << ' ';
            //    cout << endl;
            //}
            spfa();
            int ans = INT_MAX;
            for(int i = 1; i <= n; ++i)
            {
                if(dp[i][(1 << n) - 1] == -1) continue;
                ans = min(ans, dp[i][(1 << n) - 1]);
            }
            printf("%d\n",ans);
        }
        return 0;
    }

    pku1717 Dominoes

    题意:有n个骨牌,每个骨牌上有两个数字,可以通过旋转使得上下两个数字调换.求使得上方数字和与下方数字和最小的最少旋转次数

    分析:数组dp[i][j]表示到i个骨牌为止上下差值为j时的最小旋转次数,当然,与背包问题类似,只要一个一维数组即可。注意,差值可能为负,所以需要再加上一个数,将其变为正

    pku1717
    #include<iostream>
    #include<algorithm>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    using namespace std;
    
    const int N = 1000 + 10;
    const int M = 12020 + 10;
    
    int dp[2][M];
    int a[N];
    
    int main()
    {
        int n, low;
        int c, d;
        while(scanf("%d",&n) == 1)
        {
            low = 1;
            for(int i = 0; i < n; ++i)
            {
                scanf("%d %d",&c,&d);
                a[i] = c - d;
                low += abs(a[i]);
            }
            int m = low * 2;
    
            for(int i = 0; i <= m; ++i)
                dp[0][i] = INT_MAX;
    
            dp[0][low + a[0]] = 0;
            dp[0][low - a[0]] = 1;
            for(int i = 1; i < n; ++i)
            {
                for(int j = 0; j <= m; ++j)
                    dp[i & 1][j] = INT_MAX;
    
                for(int j = 0; j <= m; ++j)
                {
                    if(dp[(i - 1) & 1][j] == INT_MAX)
                        continue;
    
                    dp[i & 1][j + a[i]] = min(dp[i & 1][j + a[i]], dp[(i - 1) & 1][j]);
                    dp[i & 1][j - a[i]] = min(dp[i & 1][j - a[i]], dp[(i - 1) & 1][j] + 1);
                }
            }
            int ans = INT_MAX;
    
            for(int i = 0; i <= low; ++i)
            {
                if(dp[(n - 1) & 1][low - i] != INT_MAX || dp[(n - 1) & 1][low + i] != INT_MAX)
                {
                    ans = min(dp[(n - 1) & 1][low - i], dp[(n - 1) & 1][low + i]);
                    //cout << i << endl;
                    break;
                }
            }
            printf("%d\n",ans);
        }
        return 0;
    }

    pku1837 Balance

    题意:在一个天平上,要求将左右砝码都挂上,使得左右平衡的方法数。

    分析:与上面一道题目类似,dp[i][j] 表示挂上前i个砝码,平衡度为j时的方法数

    pku1837
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    
    using namespace std;
    
    const int N = 25;
    
    int dp[N][10010];
    int w[N],c[N];
    
    //dp[i][j] 表示 挂前i 个砝码的平衡度为j , j == 5000 表示左右平衡, j < 5000  表示左边偏重, j > 5000 表示右边偏重
    int main()
    {
        int n, m;
        while(scanf("%d %d",&n,&m) == 2)
        {
            for(int i = 1; i <= n; ++i)
                scanf("%d",&c[i]);
            for(int i = 1; i <= m; ++i)
                scanf("%d",&w[i]);
            memset(dp,0,sizeof(dp));
            dp[0][5000] = 1;
            for(int i = 1; i <= m; ++i)
            {
                for(int j = 0; j <= 10000; ++j)
                {
                    if(dp[i - 1][j] > 0)
                    {
                        for(int k = 1; k <= n; ++k)
                            dp[i][j + w[i] * c[k]] += dp[i - 1][j];
                    }
                }
            }
            printf("%d\n",dp[m][5000]);
        }
        return 0;
    }

    pku2018 Best Cow Fences

    题意:典型的数型结合的题目

    周源的论文上有详细的理论解法

    pku2018
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    
    using namespace std;
    
    const int N = 100000 + 10;
    
    int q[N], sum[N];
    
    inline bool cmp(int s1, int len1, int s2, int len2)
    {
        return s1 * 1ll * len2 < s2 * 1ll * len1;
    }
    
    int scan()
    {
        char ch;
        while( ch = getchar(), ch < '0' || ch > '9');
        int res = ch - '0';
        while( ch = getchar(), ch >= '0' && ch <= '9')
            res = res * 10 + ch - '0';
        return res;
    }
    int main()
    {
        int n, F, t;
        while(scanf("%d %d",&n,&F) == 2)
        {
            q[0] = sum[0] = 0;
    
            for(int i = 1; i <= n; ++i)
            {
                sum[i] = sum[i - 1] + scan();
            }
    
            int head = 0, tail = 0;
            for(int i = 1; i < F; ++i)
            {
                while(head < tail && cmp(sum[i] - sum[q[tail]], i - q[tail],sum[q[tail]] - sum[q[tail - 1]], q[tail]- q[tail - 1]))
                    --tail;
                q[++tail] = i;
            }
    
            int ans = sum[F], len = F;
            for(int i = F; i <= n; ++i)
            {
                while(head < tail && i - q[head + 1] >= F && cmp(sum[i] - sum[q[head]], i - q[head], sum[i] - sum[q[head + 1]], i - q[head + 1]))
                    ++head;
                
                while(head < tail && cmp(sum[i] - sum[q[tail]], i - q[tail], sum[q[tail]] - sum[q[tail - 1]], q[tail] - q[tail - 1]))
                    --tail;
                q[++tail] = i;
    
                if(i - q[head] >= F && cmp(ans, len, sum[i] - sum[q[head]], i - q[head]))
                {
                    ans = sum[i] - sum[q[head]];
                    len = i - q[head];
                }
            }
            printf("%d\n",1000 * ans / len);
        }
        return 0;
    }

    pku2184 Cow Exhibition

    题意:给n组数,每组数中有S,T值,在其中选取若干组使其所有S+T和最大,要求左边S的和非负,且右边T的和非负

    分析:背包的灵活运用,用dp[j]数组,下标表示S的值,dp[j] 的值表示S==j时T的最大值

    pku2184
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    
    using namespace std;
    
    const int N = 100 + 10;
    const int M = 200000 + 10;
    
    int dp[M];
    int s[N], f[N];
    
    int main()
    {
        int n;
        while(scanf("%d",&n) == 1)
        {
            int low = 0, high = 0;
            for(int i = 0; i < n; ++i)
            {
                scanf("%d %d",&s[i],&f[i]);
                if(s[i] < 0) low -= s[i];
                else high += s[i];
            }
            int m = low + high;
    
            for(int i = 0; i <= m; ++i)
                dp[i] = INT_MIN;
            dp[low] = 0;
            for(int i = 0; i < n; ++i)
            {
                if(s[i] < 0){
                    for(int j = 0; j <= m; ++j)
                    {
                        if(j + s[i] >= 0 && dp[j] != INT_MIN)
                            dp[j + s[i]] = max(dp[j + s[i]], dp[j] + f[i]);
                    }
                }
                else {
                    for(int j = m; j >= 0; --j)
                    {
                        if(j + s[i] <= m && dp[j] != INT_MIN)
                            dp[j + s[i]] = max(dp[j + s[i]], dp[j] + f[i]);
                    }
                }
            }
            int ans = -1;
            for(int i = low; i <= m; ++i)
            {
                int tmp =  i - low + dp[i];
                if(dp[i] >= 0  && tmp> ans)
                    ans = tmp;
            }
            printf("%d\n",ans);
        }
        return 0;
    }

     hdu3905 Sleeping

    题意:一节课有N分钟,ZZ需要睡M分钟,每分钟都有其效益值,ZZ若听课,则必须连续听L分钟以上。问能获得的最大效益值。M分钟的睡眠可以不连续

    分析:用数组dp[i][j] 表示前i分钟睡j分钟获得的最大值,当然,只有这样是没办法完成状态的转移的,还需要一个辅助数组v[j],表示前i分钟到至少i-L分钟不睡获得的最大价值。

    所以v[j] = max( v[j] + a[i], dp[i - L][j] + sum[i] - sum[i - L]) 

    hdu3905
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    
    using namespace std;
    
    const int N = 1000 + 10;
    
    int dp[N][N], a[N], v[N];
    int sum[N];
    //dp[i][j]表示前i分钟睡了j分钟的最大价值
    //v[j] 表示前i 到至少i-L分钟都清醒是获得的最大价值
    int main()
    {
        int n, m, L;
        while(scanf("%d %d %d",&n,&m,&L) == 3)
        {
            sum[0] = 0;
            for(int i = 1; i <= n; ++i)
            {
                scanf("%d",&a[i]);
                sum[i] = a[i] + sum[i - 1];
            }
            dp[0][0] = 0;
            memset(v,0,sizeof(v));
            for(int i = 1; i <= n; ++i)
            {
                for(int j = 0; j <= i && j <= m; ++j)
                {
                    dp[i][j] = dp[i - 1][j - 1];//继续睡觉
    
                    if( i - j >= L)
                    {
                        v[j] = max(v[j] + a[i], dp[i - L][j] + sum[i] - sum[i - L]);
                        dp[i][j] = max(dp[i][j], v[j]);
                    }
                }
            }
            printf("%d\n",dp[n][m]);
        }
        return 0;
    }

     pku1636 Prison rearrangement

    题意:一直有俩个监狱,分别关着m个犯人,已知r对关系,监狱1中的犯人xi 与 监狱2中的犯人yi 不能呆在同一个监狱。问,在满足以上r对关系的前提下,求最大的k(K <= M/2) ,监狱1有k个人可以和监狱2中的k个人交换

    分析:先将必须同时交换的监狱1 和 监狱2 中的犯人分组,记录每组中,分别属于监狱1 和监狱 2 的人数

    如case3 

    8 12
    1 1
    1 2
    1 3
    1 4
    2 5
    3 5
    4 5
    5 5
    6 6
    7 6
    8 7
    8 8
    接到的分组情况是这样的:

    1 4
    4 1
    2 1
    1 2

    得到四组,每组的第一个数字表示属于第一个监狱的人数,第二个数字表示属于第二个监狱的人数。

    将这四组进行一个01背包,dp[i][j] 为true表示可以将监狱1中的i个人跟监狱2中的j个人交换

    求最大i 满足dp[i][i] 为true即可 

    pku1636
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<vector>
    
    using namespace std;
    
    const int N = 400 + 10;
    
    
    pair<int, int> p[N];
    vector<int> g[N];
    
    int num, n;
    bool vis[N];
    int dp[N][N];
    
    void dfs(int s)
    {
        vis[s] = true;
        if(s > n) p[num].second++;
        else p[num].first++;
        for(int i = 0; i < g[s].size(); ++i)
        {
            int v = g[s][i];
            if(!vis[v]) dfs(v);
        }
    }
    int main()
    {
        int m, a, b;
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n,&m);
            for(int i = 0; i <= n * 2; ++i)
                g[i].clear();
            for(int i = 0; i < m; ++i)
            {
                scanf("%d %d",&a,&b);
                g[a].push_back(b + n);
                g[b + n].push_back(a);
            }
            memset(vis,false,sizeof(vis));
            num = 0;
            for(int i = 1; i <= 2 * n; ++i)
            {
                if(!vis[i])
                {
                    p[++num] = make_pair(0,0);
                    dfs(i);
                }
            }
            //for(int i = 1; i <= num; ++i)
            //    cout << p[i].first << ' ' << p[i].second << endl;
            memset(dp,false,sizeof(dp));
            m = n >> 1;
            dp[0][0] = true;
            for(int i = 1; i <= num; ++i)
            {
                for(int j = m; j >= 0; j--)
                    for(int k = m; k >= 0;  --k)
                        if(dp[j][k] && j + p[i].first <= m && k + p[i].second <= m)
                        {
                            int tmp1 = j + p[i].first, tmp2 = k + p[i].second;  
                            dp[tmp1][tmp2] = true;
                        }
            }
            for(int i = m; i >= 0; --i)
            {
                if(dp[i][i])
                {
                    printf("%d\n",i);
                    break;
                }
            }
        }
        return 0;    
    }
    
    
  • 相关阅读:
    数据库之01-数据库概述
    Bootstrap框架
    jQuery
    补充:html速查表
    BOM,DOM相关案例
    BOM,DOM
    函数,词法分析,内置对象和方法
    前端 之 JaveScript 基础语法: 数据类型; 运算符; 数据转换; 流程控制; 常用内置对象;
    favicon.ioc使用以及注意事项
    redux-undo
  • 原文地址:https://www.cnblogs.com/nanke/p/2951008.html
Copyright © 2011-2022 走看看