zoukankan      html  css  js  c++  java
  • 动态规划(入门,滚动数组,记录的都是状态):SWUSTACM-1010 魔兽争霸之最后的反击

    题目:

                                                                                        1010: 魔兽争霸之最后的反击
                                                                            Time Limit: 1000 MS Memory Limit: 65536 KB
    Description
    相传人族与兽族对峙了很久,双方均受到了重创,兽族趁人类没有能力发起大规模进攻之时突然袭击,想一次彻底打败人族。人类为了生存,无论老幼伤病,全部参战,兵分两路抗敌。
    由于体质不同,我们以血量表示一个人的战斗力,现在给你所有人的血量,请你把人类分成战斗力最接近的两部分。注意,战斗力要最接近,不然,人族会因你而战败呦!

    Input
    第一行为一个整数n(1<=n<=36),表示人族的总人数。以下的n行每行一个整数,表示一个人的血量mi(即战斗力),其中1<=mi<=400.


    Output
    只有一行,包含两个数,即人族的每部分兵的血量总和,较小的一个值放在前面,两个数用空格分隔。


    Sample Input
    3
    20
    32
    35


    Sample Output
    35 52


    Source
    SWUST



    解题心得:

    1、可以直接价将最大的战斗值/2,然后动态规划就行了。在规划的过程中状态有几种转移的方法。注意此题记录的都是状态(只有false和true),因为记录的是状态所以开的数组直接是bool类型就可以了,第一维是记录的物品的个数,第二维记录的是在选择第k个物品(人族的群数)的时候有多少种可能的重量(战斗力)。当可以达到这个重量的时候将这个重量记录为true。


    第一种:

    就是很简单的记录人群的数量和总的战斗力,直接给代码.

    关于核心的动态规划解释一下:第k件物品,可以选择不装入,所以选择第k件物品的重量就是和第k-1个相同物品的状态相同。也可以选择装入,装入就必须第前一个状态【k-1】的 j-t[j] 要是true才可以转移到这个状态。

    #include<bits/stdc++.h>
    using namespace std;
    bool dp[40][16000];//只是记录的状态bool就可以了,甚至bitset也可以;
    int t[40];
    int main()
    {
        int n;
        int sum ;
        while(~scanf("%d",&n))
        {
            memset(dp,0,sizeof(dp));
            sum = 0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&t[i]);
                sum += t[i];
            }
            dp[0][0] = 1;
            for(int i=1;i<=n;i++)
            {
                for(int j=sum;j>=0;j--)
                {
                    for(int k=1;k<=n;k++)
                        dp[k][j] = dp[k-1][j] | dp[k-1][j-t[k]];//这个是核心算法
                }
            }
            for(int i=sum/2;i>=0;i--)
            {
                if(dp[n][i])
                {
                    printf("%d %d
    ",i,sum-i);
                    break;
                }
            }
        }
    }
    


    第二种:使用滚动数组优化
    因为只有前后的状态转移所以数组的第一维只需要开2就可以了,因为true都不变(可以选择不放入),所以状态的转移是将下一个的false在上一个的true之上改变为true。说不清楚了,直接贴代码。
    #include<bits/stdc++.h>
    using namespace std;
    bool dp[2][16000];
    int t[40];
    int main()
    {
        int n;
        int sum ;
        while(~scanf("%d",&n))
        {
            memset(dp,0,sizeof(dp));
            sum = 0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&t[i]);
                sum += t[i];
            }
            dp[0][0] = 1;
    	int now;
            for(int i=1;i<=n;i++)
            {
                for(int j=sum;j>=0;j--)
                {
                    if(j >= t[i])
                        dp[i%2][j] = dp[(i+1)%2][j] | dp[(i+1)%2][j-t[i]];
                    else
                    {
                        if(dp[(i+1)%2][j])
                            dp[i%2][j] = 1;
                    }
                }
                now = i;//用来记录最后的那个数组,写的好弱智;
            }
            for(int i=sum/2;i>=0;i--)
            {
                if(dp[now%2][i])
                {
                    printf("%d %d
    ",i,sum-i);
                    break;
                }
            }
        }
    }
    

    第三种:开一个一维数组直接转移就可以了

    其实思想也很简单,这个状态转移不是从前面一次到后面转移,而是从后面往前面转移,所以后面的状态不会影响前面的状态,直接转移就可以了。

    #include<bits/stdc++.h>
    using namespace std;
    bool dp[16000];
    int t[40];
    int main()
    {
        int n;
        int sum ;
        while(~scanf("%d",&n))
        {
            memset(dp,0,sizeof(dp));
            sum = 0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&t[i]);
                sum += t[i];
            }
            dp[0] = 1;
      	for(int i=1;i<=n;i++)
                for(int j=sum;j>=0;j--)
                {
                    dp[j] = dp[j] | dp[j-t[i]];
                }
            for(int i=sum/2;i>=0;i--)
            {
                if(dp[i])
                {
                    printf("%d %d
    ",i,sum-i);
                    break;
                }
            }
        }
    }
    



  • 相关阅读:
    高度百分比生效的方法height:100%
    您可以尝试改变浏览器大小,这个元素依然是水平垂直居中的哦!
    百分比
    百分比布局
    mysql 用户管理
    lnmp安装--linux通过tar.gz源码包安装mysql
    linux内核编译,内核参数修改
    CentOS安装mplayer
    x86与x64与x86_64
    centos 关闭触摸板,触摸板点击
  • 原文地址:https://www.cnblogs.com/GoldenFingers/p/9107364.html
Copyright © 2011-2022 走看看