独立任务最优调度问题
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,2n] }
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))