算法考试专项复习笔记三(DP)

独立任务最优调度问题

p[i][k]表示前k个任务可由A在 i 时间内且B在 p[i][k] 时间内完成。
由A机完成,则有:p[i][k] = p[i-ak][k-1] (B机时间不变)
由B机完成,则有:p[i][k] = p[i][k-1] + bk (A机时间不变)
综上
p[i][k]=min(p[i][k] = p[i-a[k]][k-1],p[i][k] = p[i][k-1] + b[k])

for (int k = 1; k <= n; k++){  //代表作业
		sa += a[k];       //假设都在A上运行,得出时间和sa(数组打底)

		for (int i = 0; i <= sa; i++){
			p[i][k] = p[i][k - 1] + b[k];
			if (i >= a[k]){             //当前时间是否满足其在机器a上运行
				p[i][k] = min(p[i - a[k]][k - 1], p[i][k - 1] + b[k]);
			}
		}
	}
	for (int i = 0; i <= sa; i++){  //遍历p数组,p[i][n]的值是n个作业
		int t = max(p[i][n],i);     //在B上运行时间,i意思是n个作业在A上运行时间
		if (minTime>t)
			minTime = t;
	}

石子合并问题

状态转移方程:
在这里插入图片描述
sum(L,R)=sum( R )-sum(L-1)

	//初始化输入石头信息和算出sum
	for(i=1;i<=n;i++){
		scanf("%d",&stone[i]);
		sum[i]=sum[i-1]+stone[i];
	}
    //初始化,因为答案是最小代价,所以把所有位置初始化最大值
    memset( f , 0x3f , sizeof f );
    //石子自己和自己,因不能进行合并产生代价,所以本身代价为0
    for( int i = 1 ; i <= n ; i++ ) f[i][i] = 0 ;
    //区间dp在计算时,中间部分的必须提前计算.
    //因而枚举长度从小到大.
    for( int Len = 2 ; Len <= n ; Len ++ ){
        //枚举左端点
        for( int L = 1 ; L + Len - 1 <= n ; L++ ){
            //算出右端点
            int R = L + Len - 1 ;
            //枚举中间断点
            for( int k = L ; k < R ; k ++ ){
                f[L][R] = min( f[L][R] , f[L][k] + f[k+1][R] + sum[R] - sum[L-1] );
            }
        }
    }

石子合并问题(环形)

环形把我们n个为一排,变成 2n 为一排,最后答案必定是1~n分别作为左端点长度为n的区间
answer=min{ f[1,n] , f[2,n+1] , f[3,n+2]……f[n,2
n] }

for( int i = 1 ; i <= n ; i++ ){
        scanf("%d",&stone[i]) ;
        stone[i+n] = stone[i] ;
    }
    for( int i = n ; i <= 2 * n ; i++ ){
        sum[i] = sum[i-1] + stone[i] ;
    }
    memset( f , 0x3f , sizeof f );
    for( int i = 1 ; i <= 2 * n ; i++ ) f[i][i] = 0 ;
    for( int Len = 2 ; Len <= n ; Len ++ ){
        for( int L = 1 ; L + Len - 1 <= 2*n ; L++ ){
            int R = L + Len - 1 ;
            for( int k = L ; k < R ; k ++ ){
                f[L][R] = min( f[L][R] , f[L][k] + f[k+1][R] + sum[R] - sum[L-1] );
            }
        }
    }
    int ans = 0x3f3f3f3f ;//大
    for( int i = 1 ; i <= n ; i++ ){
        ans = min( ans , f[i][i+n-1] );
    }

矩阵连乘问题

f[L][R]=min( f[L][R] , f[L][k] + f[k+1][R] + A[L-1]*A[k]*A[R] )
具体实现过程可参考上题石子合并问题,大致相同,所以不在赘述。

0-1背包的DP问题

状态转移方程为:
在这里插入图片描述

int n,v;
int v[N],w[N];
//枚举物品
for(i=1;i<=n;i++){
	//枚举背包容量
	for(j=1;j<=n;j++){
		if(j>=v[i]){
			f[i][j]=max(f[i-1][j-v[i]]+w[i],f[i-1][j])
		}
		else{
			f[i][j]=f[i-1][j];
		}
	}
}

最长公共子序列问题

状态转移方程为:
当两个字符相同时
f[i][j]=f[i-1][j-1]+1
否则
f[i][j]=max(f[i-1][j],f[i][j-1])
其含义为:
在匹配第i个和第j个相符时,我们可以把问题转移到 f(i-1,j-1),同时长度+1。
否则,问题抛给 f( i - 1 , j ) , f( i , j - 1 )

char a[N],b[N];//两个数组
for(int i=1;i<=n;i++){
	for(int j=1;j<=n;j++){
		if(a[i]==b[j])
			f[i][j]=f[i-1][j-1]+1;
		else
			f[i][j]=max(f[i-1][j],f[i][j-1]);
	}
}		

最大字段和问题

状态转移方程为:
f[i]=max(f[i-1],0)+a[i]

int a[i] //数的数组
int ans=-0x3f3f3f3f //最终结果,初始值设置为非常小
for(int i=1;i<=n;i++){
	f[i]=max(f[i-1],0)+a[i]; //就是看是从新开始还是承接前面的和,与0比较是有正的则承接
	ans=max(ans,f[i]);
}

编辑距离问题(似乎可能不考)

状态转移方程为:
增加一个字符
f[i][j]=f[i][j-1]+1
删除一个字符
f[i][j]=f[i-1][j]+1
修改一个字符
f[i][j]=f[i-1][j-1]+(a[i]!=b[j])
f[i][j]就是以上三种情况取最小值即可
f[i][j]=min(f[i][j]=f[i-1][j-1]+(a[i]!=b[j]),min(f[i][j]=f[i][j-1]+1,f[i][j]=f[i-1][j]+1))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值