第十一周小结

  这周主要看了动态规划的博客。动态规划是分阶段求最优值的算法。看一个问题能否用动态规划求解,就是判断该问题是否具有最优子结构性质。动态规划的步骤大体包括分阶段,建立状态转移方程,递推求解。在阅读博客的过程中,我发现没有思路的题一般是推不出状态转移方程,感觉状态转移方程是解题的核心,这部分我还需要着重练习一下,通过看题练题积累。下面总结一下这周看过的题中体会较深的题目。

一、
1.Cow Bowling

题意:输入n层数字三角形,求从第一层到最后一层最大权值和。

题解:很简单的dp题,找到状态转移方程即可。从第一行的最大值(设为 dp[i][j])开始,则上一行可以转移的状态为两个:dp[i+1][ j ],dp[i+1][j+1];由此可得到状态转移方程 dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j]。比较明显的根据此递推关系可求得最大权值和。

2.P1025 [NOIP2001 提高组] 数的划分

题意:将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。

题解:首先判断出这道题可以用dp求解,接下来找状态转移方程。用f[i][j] 表示 i 分成 j 个非空的数的方案数。可以看出 当 i<x 时 f[i][j]=0 , i=x 时 f[i][j]=1。
可能出现的情况有两种:
①包含1的: f[i-1][j-1]
②不包含1的: f[i-j][j] (需要满足i 大于 j)
可得状态转移方程为: f[i][j]=f[i-1][j-1]+f[i-x][j]。

  这道题不算难,找到状态转移方程是关键。在看题解时,看到有人用搜索解题,可见解题往往不囿于一种解法,我们在思考问题时要拓宽思路,多多思考。在这里复习一下搜索,写一写搜索的解法。

#include<iostream>
using namespace std;

int n,k,cnt;

void dfs(int last,int sum,int cur)
{
    if(cur==k)
    {
        if(sum==n) cnt++;
        return;
    }
    for(int i=last;sum+i*(k-cur)<=n;i++)
        dfs(i,sum+i,cur+1);
}

int main()
{
    cin>>n>>k;
    dfs(1,0,0);
    cout<<cnt<<endl;
}

  1. P1879 [USACO06NOV]Corn Fields G

题意:有M行N列的长方形,每个小格视作正方形土地,1为可种草,0为不可种草,并且没有哪两块草地有公共边。求总的种植方案。

题解:这道题用到了状态压缩dp。 洛谷上的一篇题解写的很好,开始对这道题没有思路,看了题解发现会用到状压dp。
1、先预处理第i行的草地状态a[i],压缩为一个整数。
2、再预处理第i行不相邻的状态bl[i],每行共有(1<<m)-1种状态。
可以用(i&(i<<1)= =0)&&(i&(i>>1)= =0)来判断,如果存在相邻情况则将b[i]置为false,不存在相邻则为true。
3、对于第i行的地形init[i]和某一状态j,如果枚举到的a[i]&j= =j即说明了两个状态完全相同,没有放到贫瘠草地的情况。
4、对于第i行不和i-1行有连通草地,枚举上一行方案k,该行方案j&k==0即可满足条件。

代码:

#include <bits/stdc++.h>
#include <cstdio>
#include <algorithm>
using namespace std;
const int mod = 100000000;
int n, m;
int a[13][13];
int F[13];
int f[13][1 << 12 + 5];
bool g[1 << 12 + 5];
int main()
{   cin>>m>>n;
	for (int i = 1; i <= m; i++)
{   for (int j = 1; j <= n; j++)
    {  cin>>a[i][j];
       F[i] = (F[i] << 1) + a[i][j];
    }
}
	for (int i = 0; i < (1 << n); i++)
    	g[i] = (!(i & (i << 1))) && (!(i & (i >> 1)));
	f[0][0] = 1;
	for (int i = 1; i <= m; i++)
{   for (int j = 0; j < (1 << n); j++)
   {  if (g[j] && ((j & F[i]) == j))
    {  for (int k = 0; k < (1 << n); k++)
        {  if ((k & j) == 0)  f[i][j] = (f[i][j] + f[i - 1][k]) % mod;  }
    }
   }
}
    int ans = 0;
	for (int i = 0; i < (1 << n); i++)
      ans = (ans + f[m][i]) % mod;
	cout<<ans<<endl;
	return 0;
}

  1. P1164 小A点菜

题意:有M元N种菜,第 i 种卖a[ i ] 元,每种菜只有一份。小A点单一定刚好把uim身上所有钱花完。求有多少种点菜方法。小A最多只能等待1秒。

题解:用搭配解题,先分析题目找到状态转移方程。
设 dp[i]表示钱数为 i元时的方案数,菜的价钱为 a[i] ,
那么dp[i]+=dp[ i−a[i] ]
即 当前钱数的方案数 就等于 当前钱数的方案数 与 当前钱数减去菜价所剩钱数 的方案数 的和。注意因为每种菜只有一个,所以要从我们最多的钱数向当前菜品+1的价钱进行操作。最后处理当前菜品的价格,使当前菜品价钱对应的方案数+1。

代码:

#include<bits/stdc++.h>
using namespace std;
int m,n,a,dp[10010];
int main()
{  cin>>n>>m;
	for(int i=1;i<=n;++i)
    {   cin>>a;
		for(int j=m;j>a;j--)
        dp[j]+=dp[j-a];
		dp[a]++;
	}
	cout<<dp[m]<<endl;
	return 0;
}

  1. 1065 最小正子段和

题意:N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子段使其和>0,并且这个和是所有和>0的子段中最小的。

题解:若前后两个相邻数之和符合条件即为最优解。每输入一个数求一次和,将得到的和排序,取其最小值输出。

小总结:
  这些天博客看下来,对动态规划有点小收获。感觉解题的关键是找对状态转移方程。拿到问题首先分析,将大问题分成一个个递进的子问题,分析子问题的状态联系,找到递推关系,进而推出状态转移方程,题目就基本上能解出来了。结合上周看过的贪心,觉出了dp和贪心一点不同之处。贪心都是做出的决策是不可撤回的,而在dp中还有考察每个最优决策子序列中是否包含最优决策子序列。贪心中每一步都只顾眼前(局部)最优,而dp在选择的时候基于以前求出的与本步骤相关的子问题中的最优的那个,加上这一步的值来求得最优解。
  有关dp的题还需要再看,积累的还不够,下周继续学习dp,争取a题,下功夫练熟练透,再接再厉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值