HDU 1160

很生气的一道题…WA了好久都觉得自己是对的…debug了好久…发现是路径输出的时候发生了错误…本来是用

while(y!=0)
{
    printf("%d\n",m[y].id);
    y=pre[y];
}

这样来输出的,结果WA,然后用递归

void print(int pos)
{
    if(pos==0)return;
    print(pre[pos]);
    printf("%d\n",m[pos].id);
}

就AC了,不是说没有顺序规定的吗..,在这种条件下讲道理应该是一样的呀…究竟是怎么回事呢….哦…题目有坑…我以为是没有顺序的…其实这个任意一种的意思还是要按顺序的…有毒…题目都不讲清楚…

HDU 1024

题意:定一个数组,求其分成m个不相交子段和最大值的问题。

解题思路:设dp[i][j]表示前j个元素分成i组的最优解(j分好的组里面)
方程为dp[i][j]=max{d[i][j-1]+a[j],max(dp[i-1][k]+a[j])}(i-1<=k<=j-1)
第一种情况是j还在第i组里的最后一个
第二种情况是j自成一组,上一组最后一个为k,即第i-1组最大和
内存不够,就用滚动数组,观察到max( dp[i-1][k] ) 就是上一组 0....j-1 的最大值。我们可以在每次计算dp[i][j]的时候记录下前j个的最大值 用数组保存下来  下次计算的时候可以用.
方程就变成dp[i][j]=max(dp[j-1],pre[j-1])+a[j]。

核心代码:

int pre[1000010];//存上一层前j个的dp[k]的最大值
        for(i=1;i<=m;i++)
        {
            MAX=-INF;
            for(j=i;j<=n;j++)   //k的左界是i-1
            {
                dp[j]=max(dp[j-1],pre[j-1])+a[j];   //k的右界是j-1
                pre[j-1]=MAX;   //这一层的前j-1个的dp[k]的最大值
                MAX=max(MAX,dp[j]);   //把MAX更新一下
            }
        }
        printf("%d\n",MAX);
HDU 1114
HDU 2859

要注意可能有1*1这种数据…不能把ans初始化为-INF啊…

HDU 1074

状态压缩DP。

位运算在状压DP常见的应用如下:

1.判断一个数字x二进制下第i位是不是等于1。

方法:if ( ( ( 1 << ( i - 1 ) ) & x ) > 0)

将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x = x | ( 1<<(i-1) )

3.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x-1)

代码及注释:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
#define MAXN (1<<15)+10
using namespace std;
struct task
{
    char name[110];
    int limit,actu;
}t[20];
int dp[MAXN],Time[MAXN],path[MAXN];
//dp表示在此状态扣了多少分
//Time表示在此状态用了多少时间
//path表示到达状态i的前驱,为了最后输出完成作业的顺序
void output(int x)
{
    if(x==0)return;
    output(x-(1<<path[x]));
    printf("%s\n",t[path[x]].name);
}
int main()
{
    int T,i,j,n,tot,temp,score;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%s%d%d",t[i].name,&t[i].limit,&t[i].actu);
        memset(Time,0,sizeof(Time));
        int tot=1<<n;//所有状态
        for(i=1;i<tot;i++)
        {
            dp[i]=INF;
            for(j=n-1;j>=0;j--)//有两门作业完成后分数相同,在这种情况下,起初位于前面的作业位于前面
            {
                int temp=1<<j;
                if(!(i&temp))continue;//第j门没做
                int score=Time[i-temp]+t[j].actu-t[j].limit;//讲道理也不是很懂为什么是i-temp...
                if(score<0)score=0;
                if(dp[i-temp]+score<dp[i])//若最后做第j门作业,所扣的分数少于之前的分数
                {
                    dp[i]=dp[i-temp]+score;//作为此状态的最优解
                    Time[i]=Time[i-temp]+t[j].actu;//到达状态i花费的时间
                    path[i]=j;
                }
            }
        }
        printf("%d\n",dp[tot-1]);
        output(tot-1);
    }
    return 0;
}
POJ 3666

题意:给出长度为n的整数数列,每次可以将一个数加1或者减1,最少要多少次可以将其变成单调增或者单调减(不严格)。

解题思路:对于某个位置而言,一定改成和他前一个一样,或者和他后一个一样,所以最后形成的序列的数都在原序列里出现过,考虑b数组保存排完序后的原数组值,dp[i][j] = min(dp[i-1][k])+a[i]-b[j], 1<=k<=j。dp[i=当前考虑的前i个数][j=第i个数在总序列中排第j小]=当前的情况的最小改动量。什么意思呢,就是求当前dp[i][j]时候,考虑前i-1个数字,取第i-1个数字在第k(k=1…j)小中的最小改动量,再加上第i个数字成为第j小的改动量。
51nod 1007

题意:将一堆正整数分为2组,要求2组的和相差最小。**

解题思路:分成两组要相差最小,肯定是一组大于sum/2一组小于sum/2或者两组都是sum/2,就相当于其中一组与sum/2相差最小,然后转化为容量为sum/2的01背包问题即可。

51nod 1021

题意:现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

解题思路:

dp[i][j]:将i到j合并成一堆的最小代价
比如i=1,j=4,dp[1][4]=min(dp[1][3]+dp[4][4],dp[1][2]+dp[3][4],dp[1][1]+dp[2][4])+sum[1][4];
51nod 1084

题意:

一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。

解题思路:
从左上角到右下角再回到左上角,这个过程可以看作从左上角同时走两路到右下角,我们记录两路当前的位置,这样就可以处理“同一个格子的数只能取一次”了。但是如果用dp[x1][y1][x2][y2]记录状态,这显然不可以。因为两路是同时走得,到达某一状态,它们走过的步数是相同,而且有了步数和行号就可以确定列号了,因此用dp[x1][x2][step]记录状态就可以了。
51nod 1202

题意:对于给出序列a,有些子序列可能是相同的,这里只算做1个,请输出a的不同子序列的数量。由于答案比较大,输出Mod 10^9 + 7的结果即可。

解题思路:假设dp[i]表示前i项形成的子序列(含空)的个数。下标从1开始,初值是dp[0] = 1,对应代表空子序列。我们考虑第i项,如果所有的数都不相等,应该有dp[i] = dp[i – 1] * 2,其实就是考虑把第i个数放到最后或者不放到最后的情况,原来恰好以第j个数结尾的那些被我们算了两次,因为以第j个数结尾可以换成以第i个数结尾是一样的。如何计算出这个数的个数呢? 其实这个数等于dp[j – 1],因为前面(j – 1)个数的子序列最后跟上第j个数就可以了。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

数论基础专题 Previous
最小生成树专题 Next