每日小训练

lsnu——dp题解

1.自然数拆分(计蒜客1248)

  • 这道题和以前的一道题一摸一样,但是这道题是求具体的拆分方案
  • 这道题用深度搜索,一直去搜索直到找到满足的情况。
    有什么种实现方法 但这份代码我觉得最清楚明白
#include
using namespace std;

const int N = 1e5;
int n;
int a[N];
void dfs(int sum , int len , int beginx)//sum 选的数的和 len 选了几个数 beginx 选数的起点
{
     
    if(sum == n)
    {
     
        cout << n << "=" << a[0];
        for(int i = 1 ; i <len ; i++)
            cout << "+" << a[i];
        cout << endl;
        return ;
    }
    for(int i = 1 ; i <n ;i++)
    {
     
        if(sum + i <= n && i >= beginx)//  同一个数可以被多次选 ,又要满足单增
        {
     
            a[len] = i;
            len ++;
            dfs(sum + i , len , i);
            len --;//回溯
        }
    }

}
int main()
{
     
    cin >> n;

    dfs(0 ,0 ,1);

    return 0;
}

2.走马卒(计蒜客2118)

  • 这是我在洛谷做的第一个题 现在终于做出来
  • 看到的第一眼 可能会觉得这是一个非常模板的深度搜索题,但是只能过几个数据,会超时,那么就用更少循环的递推 DP吧
  • f[i , j]储存的走到这个点有多少种走法 。状态的转移是 f [i , j] = f[i -1 , j] + f[i , j-1] 也就是点(i , j)由左边的点走过来和上边的点走过来,这个题的状态转移还有情况就是(i ,j)只能由左边或者上边走过来(受边界影响or“马”),那么f[i , j]=f[i-1 ,j]<上面走过来>或者 f[i , j]=f[i , j-1] , 但是题中由于边界和“马”的存在还有一些特殊的情况需要讨论
  • 1 . (0 ,0~n)点也就是第一排的点 , 这一排的点只能由左边得来,同时如果在第一排中有“马” , 那么“马”后面的点也就不可能走到
//dp[i][j初始为0,前一个点不能为“马”点,如果前一个为“马”那么这个点的走法为
//0,那么后续的点也就都为0了
if(mapx[i][j] != INF && mapx[i][j-1] != INF) dp[i][j] = dp[i][j-1];
  • 2.(0~n ,0)点也就是第一列的点,和上一种情况类所
if(mapx[i][j] != INF && mapx[i-1][j] != INF) dp[i][j] = dp[i-1][j];
  • 3中间的点分别讨论 1.不能走到(上 左都是马)2.只能由一种情况走到3.左边和上边都能走到
if(mapx[i][j] == INF) dp[i][j] = 0;
else if(mapx[i-1][j] == INF && mapx[i][j-1] == INF) dp[i][j] = 0;
else if(mapx[i-1][j] == INF) dp[i][j] = dp[i][j-1];
else if(mapx[i][j-1] == INF) dp[i][j] = dp[i-1][j];
else dp[i][j] = dp[i-1][j] + dp[i][j-1];

给一个样例的路线图解
每日小训练_第1张图片
完整代码

#include

using namespace std;

const int N = 25;
const int INF = -1e5;
int bx , by , mx , my;
int mdirx[]={
     -2,-2,-1,-1,+1,+1,+2,+2};
int mdiry[]={
     -1,+1,-2,+2,-2,+2,-1,+1};
long long int mapx[N][N] , dp[N][N];//不开long long不能过

int main()
{
     
    cin >> bx >> by >> mx >> my;
    mapx[mx][my] = INF;
    for(int i = 0;i < 8 ;i++)
    {
     
        int x = mx + mdirx[i];
        int y = my + mdiry[i];
        if(x >= 0 && x <= bx && y>= 0 && y <= by)
            mapx[x][y] = INF;
    }//记录马的影响坐标
    for(int i = 0 ; i <= bx ; i++)
    {
     
        for(int j = 0 ; j <= by ; j++)
        {
     
            if(i == 0 && j == 0)dp[0][0] = 1;//初始化
            else if(i == 0)
            {
     
                if(mapx[i][j] != INF && mapx[i][j-1] != INF) dp[i][j] = dp[i][j-1];//if让整个数组的边界都+1的话可以减少很多讨论,但是这个没有优化
//的最清晰吧 大家可以想想哦dp[0][x] = 0的话+ dp[0][x]也不会影响dp[0][x+1]
            }
            else if(j == 0)
            {
     
                if(mapx[i][j] != INF && mapx[i-1][j] != INF) dp[i][j] = dp[i-1][j];
            }
            else
            {
     
                if(mapx[i][j] == INF) dp[i][j] = 0;
                else if(mapx[i-1][j] == INF && mapx[i][j-1] == INF) dp[i][j] = 0;
                else if(mapx[i-1][j] == INF) dp[i][j] = dp[i][j-1];
                else if(mapx[i][j-1] == INF) dp[i][j] = dp[i-1][j];
                else dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
           // cout << dp[i][j] << "(" << i <<"," << j << ")" << "  ";
        }
        //cout << endl;//有兴趣的可以看看不同数据的图解
    }

    cout << dp[bx][by] << endl;
    return 0;
}

我的写法肯定很劣质 , 还有更简单的更巧妙的做法,下面的链接有各种解法
https://www.luogu.com.cn/problem/solution/P1002
3.大盗阿福(计蒜客1227)
这是一个DP问题,解题关键是找到满足题目条件的状态表示。
dp[i]储存的是盗窃金额最大数,dp[i] : 前 i 个店中最优盗法 , 对于每个店都有盗或者不盗的选择(跟0 1背包问题很像)那么我们的状态
dp[ i ] = max(dp[i-1](不盗) , dp[i - 2] + a[i](盗));
注意边界 i - 2的处理;

#include
using namespace std;

const int N = 1e6 + 10;
int t;

int main()
{
     
    cin >> t;
    while(t--)
    {
     
        int n ;
        int a[N] , dp[N];
        cin >> n;
        for(int i = 1 ; i <= n ; i++)
            cin >> a[i];
        dp[1] = a[1];//i = 1直接选上,循环从i = 2开始
        for(int i = 2 ; i <= n ; i++)
            dp[i] = max(dp[i-1] , dp[i-2] + a[i]);

        cout << dp[n] << endl;
    }

    return 0;
}

4.严酷的训练(计蒜客1739)
这是一个模板的 0 1 背包问题,在规定的时间内选择出最优的做题选择
dp[ i ][j]储存的是规定时间内在前 i 个问题且限制时间为j中选择的最大得分
, 对于每个问题我们都有选 or 不选
dp[i][j] = max(dp[i-1][j] , dp[i-1][j - 时间[i]] + 得分[i]) (还可以优化)

#include

using namespace std;

const int N = 5500;
int k1,k2;
int m,n,limt;
int t[N];
int ch[N][2];
int dp[N];

int main()
{
     
    cin >> k1 >> k2;
    cin >> m >> n;
    for(int i = 1; i<=n; i++)
    {
     
        int tm;
        cin >> tm;
         t[i] = (k2 / k1) * tm;
    }//学生做第i题需要的时间
    for(int i =1; i <= m; i++)
        cin >> ch[i][0] >> ch[i][1];//记录该题的编号和得分
    cin >> limt;
    for(int i = 1; i<= m ;i++)
        for(int j = limt; j >= t[ch[i][0]]; j--)//优化
            dp[j] = max(dp[j],dp[j - t[ch[i][0]]] + ch[i][1]);
//f[i][j] = f[i-1][j],if (j > t[ch[i][0]])(for 从j=0开始)dp[i][j]
//=max(dp[i-1][j] , dp[i -1][j - t[ch[i][0]]] + ch[i][1]
    cout << dp[limt] << endl;

    return 0;
}

你可能感兴趣的:(题解,算法,oj系统,acm竞赛)