(sdau) Summary of the fifth week.

学习内容:

本次还是动态规划的学习,通过再一周的做题目,认识到做动态规划题目还是有几个固定步骤的;
1,首先要根据题目的时间和空间特征把问题分成若干阶段。(时间超时的优化问题在动态规划的本次学习中显得十分重要)
2,将问题进行到某个阶段的情况用不同的状态表示出来,且状态的选择无后效性,也就是某状态一旦确定此后的变化不再受之前的影响只和本阶段状态有关。
3,确定状态转移方程,也是最难的一步,一般情况下是根据相邻两个阶段的关系来决定如何确定本阶段*(拿从表格中的左上到右下,只能向右和向下两个方向走的问题来讲就是根据现在所处格子相邻的上面阶段和左面阶段来确定要选择那个阶段来决定本阶段)从而确定状态转移方程。
4,确定边界条件,也就是状态转移方程的一个
终止条件*,一般状态转移方程找出来也就能确定边界条件了。

所解决的问题:

E-E:

FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the speeds are decreasing.
Input
Input contains data for a bunch of mice, one mouse per line, terminated by end of file.

The data for a particular mouse will consist of a pair of integers: the first representing its size in grams and the second representing its speed in centimeters per second. Both integers are between 1 and 10000. The data in each test case will contain information for at most 1000 mice.

Two mice may have the same weight, the same speed, or even the same weight and speed.
Output
Your program should output a sequence of lines of data; the first line should contain a number n; the remaining n lines should each contain a single positive integer (each one representing a mouse). If these n integers are m[1], m[2],…, m[n] then it must be the case that

W[m[1]] < W[m[2]] < … < W[m[n]]

and

S[m[1]] > S[m[2]] > … > S[m[n]]

In order for the answer to be correct, n should be as large as possible.
All inequalities are strict: weights must be strictly increasing, and speeds must be strictly decreasing. There may be many correct outputs for a given input, your program only needs to find one.
Sample Input
6008 1300
6000 2100
500 2000
1000 4000
1100 3000
6000 2000
8000 1400
6000 1200
2000 1900
Sample Output
4
4
5
9
7

题目理解:给你鼠的体重和速度,并记录下标,求出体重递增但速度递减(可能会有多只老鼠的体重相同)的
最长子序列,输出子序列的的长度和依次输出下标即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1330;
int dp[maxn];
int k[maxn];
struct uu
{
    int x;
    int y;
    int pos;//用来标记下标
    int pre;//用来标记子序列的下标
} aa[maxn];
bool cmp(uu a, uu b)
{
    if (a.x == b.x)
        return a.y > b.y;
    return a.x < b.x;
}
int main()
{
    int i = 1, j;
    while (scanf("%d%d", &aa[i].x, &aa[i].y) != EOF)
    {
        aa[i].pos = i;
        i++;
    }
    int n = i;
    sort(aa + 1, aa + n, cmp);
    memset(dp, 0, sizeof(dp));
    for (i = 1; i < n; i++)
    {
        dp[i] = 1;
        for (j = 1; j < i; j++)
        {
            if (aa[j].y > aa[i].y&&aa[j].x<aa[i].x&&dp[i]<dp[j] + 1)
            {
                dp[i] = dp[j] + 1;
                aa[i].pre = j;
            }
        }
    }
    int maxu = 0, p = 1;
    for (i = 1; i <= n; i++)
    {
        if (maxu < dp[i])
        {
            maxu = dp[i];
            p = i;
        }
    }
    cout << maxu << endl;
    for (i = 1; i < n; i++)
    {
        k[i] = p;
        p = aa[p].pre;
    }
    for (i = maxu; i >= 1; i--)
    {
        cout << aa[k[i]].pos << endl;
    }
    return 0;
}

这道题目之前遇到过类似的,就是要熟练掌握本题中结构体中pre的用处,用来记录所求的序列本项的前一项下标,每次有更好的都会覆盖前一次的值,所以也是最优的。减少了一定的空间,时间上好像也没更复杂吧,这方面我也不太专业,以后多理解时间和空间的复杂程度。

F-F:

Jesus, what a great movie! Thousands of people are rushing to the cinema. However, this is really a tuff time for Joe who sells the film tickets. He is wandering when could he go back home as early as possible.
A good approach, reducing the total time of tickets selling, is let adjacent people buy tickets together. As the restriction of the Ticket Seller Machine, Joe can sell a single ticket or two adjacent tickets at a time.
Since you are the great JESUS, you know exactly how much time needed for every person to buy a single ticket or two tickets for him/her. Could you so kind to tell poor Joe at what time could he go back home as early as possible? If so, I guess Joe would full of appreciation for your help.
Input
There are N(1<=N<=10) different scenarios, each scenario consists of 3 lines:

  1. An integer K(1<=K<=2000) representing the total number of people;
  2. K integer numbers(0s<=Si<=25s) representing the time consumed to buy a ticket for each person;
  3. (K-1) integer numbers(0s<=Di<=50s) representing the time needed for two adjacent people to buy two tickets together.
    Output
    For every scenario, please tell Joe at what time could he go back home as early as possible. Every day Joe started his work at 08:00:00 am. The format of time is HH:MM:SS am|pm.
    Sample Input
    2
    2
    20 25
    40
    1
    8
    Sample Output
    08:00:40 am
    08:00:08 am

题目理解:
给你排队买票的人数,以及每个人买票的时间和相邻两个人一起买两张票的时间,选择用时最少的方案并输出全部买完票的时间,买票最开始的时间从8:00am开始;
题目解析:
超级简单的动态规划问题,只要选择一个人买和两个人一起买那个用时少就好的了,最后输出格式问题上面也很容易就会得出来,挺水的题。
也就是不要管怎么得到前面得步骤,你只需要知道这一步怎么得到我想要的最少的时间就好,也就是动态转移方程:dp[i]=min(dp[i-1]+s[i],dp[i-2]+d[i]);
再找出边界条件,dp[0]=0;//没有人买票所用时间便是零。dp[1]=s[1];//只有一个人买票所用时间就是这个人的时间,不可能有其他情况。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1330;
int n,k,s[2005],d[2005],dp[2005];
int main()
{
    cin>>n;
    while(n--)
    {
        memset(s,0,sizeof(s));
        memset(d,0,sizeof(d));
        memset(dp,0,sizeof(dp));
        cin>>k;
        for(int i=1;i<=k;i++)
            cin>>s[i];
        for(int i=2;i<=k;i++)
            cin>>d[i];
        dp[0]=0;dp[1]=s[1];
        for(int i=2;i<=k;i++)
            dp[i]=min(dp[i-1]+s[i],dp[i-2]+d[i]);
        int h=dp[k]/3600+8;
        int m=dp[k]/60%60;
        int s=dp[k]%60;
       printf("%02d:%02d:%02d am\n", h, m, s);
    }
}

G-G:

A project manager wants to determine the number of the workers needed in every month. He does know the minimal number of the workers needed in each month. When he hires or fires a worker, there will be some extra cost. Once a worker is hired, he will get the salary even if he is not working. The manager knows the costs of hiring a worker, firing a worker, and the salary of a worker. Then the manager will confront such a problem: how many workers he will hire or fire each month in order to keep the lowest total cost of the project.
Input
The input may contain several data sets. Each data set contains three lines. First line contains the months of the project planed to use which is no more than 12. The second line contains the cost of hiring a worker, the amount of the salary, the cost of firing a worker. The third line contains several numbers, which represent the minimal number of the workers needed each month. The input is terminated by line containing a single ‘0’.
Output
The output contains one line. The minimal total cost of the project.
Sample Input
3
4 5 6
10 9 11
0

题目理解:
给你月份数,雇佣工人的成本,工资,以及解雇公认的成本,再给你每个月最少需要的工人数量。让你求完成项目所用最少的资金数。
题目解析:
有两种情况,一种是最开始工人数在下个月多,另一种是工人数在下个月少,第二种肯定就是要再次雇佣工人了,这是肯定的,对于第一种,我们有两种情况可以选择,一是解雇他付出解雇成本,而是保留他付出工资,保留是为了如果再下个月所需工人数又上涨了,若解雇的话要再付雇佣成本,这一样就不是最少的资金数了。
所以我们便得到了状态转移方程:dp[i][j]表示第i个月由j个人做的总资金数,
if(j>=k)//表示之后所需的工人数比之前的所需工人数多需要多雇佣,
dp[i][j]=min(dp[i][j],dp[i-1][k]+jz+(j-k)g);
else
//之后的需要的少,判断是否需要解雇

dp[i][j]=min(dp[i][j],dp[i-1][k]+j*z+(k-j)*jj);
边界条件就是第一个月最小的资金数就是人数乘成本加工资。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
#define inf 999999999;
int n,g,z,jj,a[20],dp[14][1010];
int main()
{
    while(cin>>n&&n)
    {
        int maxx=0;
        cin>>g>>z>>jj;
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
            maxx=max(maxx,a[i]);
        }
        memset(dp,0x3f,sizeof(dp));
        for(int i=1; i<=maxx; i++)
            dp[1][i]=i*(g+z);
        for(int i=2; i<=n; i++)
            for(int j=a[i]; j<=maxx; j++)
                for(int k=a[i-1]; k<=maxx; k++)
                    if(j>=k)
                        dp[i][j]=min(dp[i][j],dp[i-1][k]+j*z+(j-k)*g);
                    else
                        dp[i][j]=min(dp[i][j],dp[i-1][k]+j*z+(k-j)*jj);
        int ans=inf;
        for(int i=a[n]; i<=maxx; i++)
            ans=min(ans,dp[n][i]);
        cout<<ans<<endl;
    }
}

H-H:

Given a permutation a1, a2, … aN of {1, 2, …, N}, we define its E-value as the amount of elements where ai > i. For example, the E-value of permutation {1, 3, 2, 4} is 1, while the E-value of {4, 3, 2, 1} is 2. You are requested to find how many permutations of {1, 2, …, N} whose E-value is exactly k.
Input
There are several test cases, and one line for each case, which contains two integers, N and k. (1 <= N <= 1000, 0 <= k <= N).
Output
Output one line for each case. For the answer may be quite huge, you need to output the answer module 1,000,000,007.
Sample Input
3 0
3 1
Sample Output
1
4

题目理解:
给你一个数n,和k,得到关于n的序列{1,2,3,……n}每个数的下标也是1,2,3,……n,让你求出数大于下标的个数为k的序列数有多少个;
题目解析:
还是不用管上一步怎么来的,我们只要知道这一步怎么求就好,每多一个数,一是原本已经有j个数大于下标得数,让这个数去和数大于下标的交换,也就是dp[i-1][j](j+1);另一种就是原本只有j-1个数大于下标得数,让这个数去和下标大于数的交换也就是dp[i-1][j-1](i-j);
这样我们就得到状态转移方程了:dp[i][j]=((j+1)dp[i-1][j]+dp[i-1][j-1](i-j))%mod;
边界条件就是dp[i][0]=1;一个数大于下标的都没有就是这个序列本身也就是1;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
#define mod 1000000007;
int n,k,dp[1010][1010];
int main()
{
    while(cin>>n>>k)
    {
        for(int i=1;i<=n;i++)
        {
            dp[i][0]=1;
            for(int j=1;j<=i&&j<=k;j++)
                dp[i][j]=((j+1)*dp[i-1][j]+dp[i-1][j-1]*(i-j))%mod;
        }
        cout<<dp[n][k]<<endl;
    }
}

这是第一次提交的答案,是不对的,于是再次读题,就发现序列不是输入进去的 而是事先就可以知道的,所以我们可以先求出所有情况并标记,最后只需要读入数据然后输出即可。而且前面的代码在状态转移方程的限定条件哪里似乎有点问题,样例是对的,但是可能数字大了就不太对了。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
long long dp[1005][1005];
const int mod = 1e9 + 7 + 0.1;
void init()
{
    dp[1][0] = 1;
    for(int i = 2; i < 1001; i++)
    {
        dp[i][0] = 1;
        for(int j = 1; j < i; j++)
        {
            dp[i][j] = (dp[i - 1][j - 1] * (i - j) + dp[i - 1][j] * (j + 1))%mod;
        }
    }
}
int main()
{
    init();
    int n, k;
    while(~scanf("%d%d", &n, &k))
    {
        printf("%I64d\n", dp[n][k]);
    }
    return 0;
}

I-I:

Bob is a strategy game programming specialist. In his new city building game the gaming environment is as follows: a city is built up by areas, in which there are streets, trees,factories and buildings. There is still some space in the area that is unoccupied. The strategic task of his game is to win as much rent money from these free spaces. To win rent money you must erect buildings, that can only be rectangular, as long and wide as you can. Bob is trying to find a way to build the biggest possible building in each area. But he comes across some problems – he is not allowed to destroy already existing buildings, trees, factories and streets in the area he is building in.

Each area has its width and length. The area is divided into a grid of equal square units.The rent paid for each unit on which you’re building stands is 3$.

Your task is to help Bob solve this problem. The whole city is divided into K areas. Each one of the areas is rectangular and has a different grid size with its own length M and width N.The existing occupied units are marked with the symbol R. The unoccupied units are marked with the symbol F.
Input
The first line of the input contains an integer K – determining the number of datasets. Next lines contain the area descriptions. One description is defined in the following way: The first line contains two integers-area length M<=1000 and width N<=1000, separated by a blank space. The next M lines contain N symbols that mark the reserved or free grid units,separated by a blank space. The symbols used are:

R – reserved unit

F – free unit

In the end of each area description there is a separating line.
Output
For each data set in the input print on a separate line, on the standard output, the integer that represents the profit obtained by erecting the largest building in the area encoded by the data set.
Sample Input
2
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F

5 5
R R R R R
R R R R R
R R R R R
R R R R R
R R R R R
Sample Output
45
0
题目理解:
给你一个矩阵让你搞建筑,只能用矩阵里面空闲的地方,并且你的建筑一定要是矩形,求出最大的矩形面积乘3即可
题目解析:
学习动态规划的时候看过一本acm的竞赛书,了解过最大完全矩阵的问题,跟这个应该是类似的;
这个题和之前的不太一样,不太容易找状态转移方程,而且个人感觉其实递归搜索更容易理解,但是有点麻烦,哦对了,好像递归的题目都可以用动态规划去求解,而且复杂度比较低,就是难理解一点。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int k,m,n;
int dp[1010][1010];
char x[2];
int main()
{
    cin>>k;
    while(k--)
    {
        cin>>m>>n;
        memset(dp,0,sizeof(dp));
        for(int i=1; i<=m; i++)
            for(int j=1; j<=n; j++)
            {
                cin>>x;
                if(x[0]='F')
                    dp[i][j]=dp[i-1][j]+1;
                else
                    dp[i][j]=0;
            }
        int ans[1010][1010],k=0,maxx=0;
        for(int i=1; i<=m; i++)
        {
            k=0;
            for(int j=1; j<=n; j++)
            {
                if(dp[i][j]>dp[i][j-1])
                    k++;
                for(int h=0; h<k; h++)
                    ans[i][h]+=dp[i][h];
                ans[i][k]+=dp[i][j];
            }
            for(int h=0; h<=k; h++)
                if(ans[i][h]>maxx)
                    maxx=ans[i][h];
        }
        cout<<maxx*3<<endl;
    }
}

这是第一次交的代码,完全是错的,其实一开始思路是对的,但是中间再继续判断到时候我在纸上示范样例的时候发现很多不同的情况,就比如之前是dp[i][j]>dp[i][j+1],然后后面是dp[i][j]<dp[i][j+1]就卡住了,卡了好几个小时,情况写全了发现还是不对,下面的是搜的题解,发现还是左右两边分别求的问题。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int k,m,n;
int dp[1010][1010],l[1010],r[1010];
char x;
int main()
{
    cin>>k;
    while(k--)
    {
        int maxx=-1;
        cin>>m>>n;
        memset(dp,0,sizeof(dp));
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        for(int i=1; i<=m; i++)
            for(int j=1; j<=n; j++)
            {
                cin>>x;
                if(x=='F')
                    dp[i][j]=dp[i-1][j]+1;
                else
                    dp[i][j]=0;
            }
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
                l[j]=r[j]=j;
            l[0]=1;
            r[n+1]=n;
            dp[i][0]=-1;
            dp[i][n+1]=-1;
            for(int j=1;j<=n;j++)
            {
                while(dp[i][l[j]-1]>=dp[i][j])
                    l[j]=l[l[j]-1];
            }
            for(int j = n; j >= 1; j--)
            {
                while(dp[i][r[j]+1] >=dp[i][j])
                    r[j] = r[r[j]+1];
            }
            for(int j = 1; j <=n; j++)
            {
                if(dp[i][j]*(r[j]-l[j]+1) >maxx)
                    maxx= dp[i][j]*(r[j]-l[j]+1);
            }
        }
        cout<<maxx*3<<endl;
    }
}

本周感悟:

通过再一周的动态规划学习,多接触了很多题目,有几个题是我想了好多个小时才看懂题目意思的,英文确实需要提高。还有做题速度太慢了,从白天做到晚上就没做出几个题,写代码速度问题吧,打了愚人节的cf就做出一个题,当然不是第一道,第一道我没看明白为啥,英文确实有待提高啊。再接再厉吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值