2020年4月第一周

学习内容

本周又对于动态规划进行了进一步学习,总体来说还是按照一贯的学习思路,对于问题先进行分析,很多时候解决问题的决定你醒因素往往就隐藏在题目当中,而我们需要将题目剖析完全,之后对于不同题目选取最合适的阶段,并分析其之间的联系。

在讲解题目时提到了cin cout在提交代码后会出现Time limit错误的情况,而这个问题我在上一篇博客当中也有提到;对于此问题,我又在网上查询了一些资料:

C++ 中的输入与输出可以看做是一连串的数据流,输入即可视为从文件或键盘中输入程序中的一串数据流,而输出则可以视为从程序中输出一连串的数据流到显示屏或文件中。

在编写 C++ 程序时,如果需要使用输入输出时,则需要包含头文件iostream,它包含了用于输入输出的对象,例如常见的cin表示标准输入、cout表示标准输出、cerr表示标准错误。

cout 和 cin 都是 C++ 的内置对象,而不是关键字。C++ 库定义了大量的类(Class),程序员可以使用它们来创建对象,cout 和 cin 就分别是 ostream 和 istream 类的对象,只不过它们是由标准库的开发者提前创建好的,可以直接拿来使用。这种在 C++ 中提前创建好的对象称为内置对象。

就在这时,老师讲到了一串神奇的代码:

cin.tie(0);
ios::sync_with_stdio(0);

原来而cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入 输出缓存,可以节省许多时间,使效率与scanf与printf相差无几,还有应注意的是scanf与printf使用的头文件应是stdio.h而不是 iostream。

据网友说,C++在IO方面效率并不低下,这只是为了兼容C所采取的保守措施。

在ACM里,经常出现 数据集超大造成 cin TLE的情况。这时候大部分人(包括原来我也是)认为这是cin的效率不及scanf的错,甚至还上升到C语言和C++语言的执行效率层面的无聊争论。其实,这只是C++为了兼容而采取的保守措施。我们可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout和printf 之类。

在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。

#include <iostream>
using namespace std;
int main() 
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    // IO
}

问题解决

自从见识了平安夜苹果的涨价后,Lele就在他家门口水平种了一排苹果树,共有N棵。

突然Lele发现在左起第P棵树上(从1开始计数)有一条毛毛虫。为了看到毛毛虫变蝴蝶的过程,Lele在苹果树旁观察了很久。虽然没有看到蝴蝶,但Lele发现了一个规律:每过1分钟,毛毛虫会随机从一棵树爬到相邻的一棵树上。

比如刚开始毛毛虫在第2棵树上,过1分钟后,毛毛虫可能会在第1棵树上或者第3棵树上。如果刚开始时毛毛虫在第1棵树上,过1分钟以后,毛毛虫一定会在第2棵树上。

现在告诉你苹果树的数目N,以及毛毛刚开始所在的位置P,请问,在M分钟后,毛毛虫到达第T棵树,一共有多少种行走方案数。

输入
本题目包含多组测试,请处理到文件结束(EOF)。
每组测试占一行,包括四个正整数N,P,M,T(含义见题目描述,0<N,P,M,T<100)
输出
对于每组数据,在一行里输出一共的方案数。
题目数据保证答案小于10^9
Sample Input
3 2 4 2 3 2 3 2

Sample Output
4 0

 2->1->2->1->2

2->1->2->3->2

2->3->2->1->2

2->3->2->3->2

取状态:f[i][j]=f[i-1][j-1]+f[i-1][j+1],其中I表示的是第I分钟,J表示的是当前位置,要到当前位置,可能从右边过来,也可以从左边过来;

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<iomanip>
#include<iostream>
#define INF 0x3f3f3f3f
#define MAX 2005
using namespace std;
 
int main()
{
	int n,p,m,t;
	int ans[101][101];
	while(cin>>n>>p>>m>>t)
	{
	    memset(ans,0,sizeof(ans));
        if(p<n)ans[1][p+1]=1;
        if(p>1)ans[1][p-1]=1;
		for(int i=2;i<=m;i++)
			for(int j=1;j<=n;j++)
				ans[i][j]=ans[i-1][j-1]+ans[i-1][j+1];
		cout<<ans[m][t]<<endl;
	}
	return 0;
}

题目概述:

我们有俩种操作,可以雇佣人和开除人,每种操作都有一定的花费,每个月还得给在职的员工发工资,每个月有一个最小员工数(不能低于它),问n个月的最小花费

输入

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'. 

输出

The output contains one line. The minimal total cost of the project. 

Sample Input


4 5 6
10 9 11
0
Sample Output
199

题目分析:很明显的dp[i]保存第i个月的最小花费,但是这样显然不够,因为你无法转移到下一个状态,还有一个关键的元素是人数.

知道这个月的人数和dp[i]转移到下一个月的人数和dp[i+1]非常好写了,少人就加多人就减。

每个人的人数是从最小员工数到(题目出现的最小员工数)超过它就是浪费钱

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<iomanip>
#include<iostream>
#define INF 0x3f3f3f3f
#define maxn 15
using namespace std;
struct note
{
    int cost,num;
};
struct note dp[maxn][200];
int a[maxn],Num[maxn];
int main()
{
    int n;
    while (scanf("%d",&n)!=EOF) {
        if (n==0) break;
        int p,q,r,Max=0;
        scanf("%d%d%d",&p,&q,&r);
        for (int i=1;i<=n;i++) {
            scanf("%d",&a[i]);
            Max=max(Max,a[i]);
        }
        memset (Num,0,sizeof (Num));
        for (int i=1;i<=n;i++) {
            for (int j=a[i];j<=Max;j++) {
                if (i==1) {
                    struct note sum;
                    sum.cost=j*p+j*q;
                    sum.num=j;
                    dp[i][Num[i]++]=sum;
                }
                else {
                    int ans=0x3f3f3f3f;
                    struct note sum;
                    sum.num=j;
                    for (int k=0;k<Num[i-1];k++) {
                        int s1;
                        if (dp[i-1][k].num>=j) {
                            s1=(dp[i-1][k].num-j)*r;
                        }
                        else {
                            s1=(j-dp[i-1][k].num)*p;
                        }
                        s1+=j*q;
                        s1+=dp[i-1][k].cost;
                        ans=min(ans,s1);
                    }
                    sum.cost=ans;
                    dp[i][Num[i]++]=sum;
                }
            }
        }
        int ans=0x3f3f3f3f;
        for (int i=0;i<Num[n];i++) ans=min(ans,dp[n][i].cost);
        printf("%d\n",ans);
 
    }
    return 0;
}

输入数据的第一行有一个整数N表示子弹和恐怖分子的类型数。随后的一行是各种恐怖分子类型的一行字母,两个字母之间没有任何字符。接下来的一行是击毙上一行对应位置恐怖分子类型的得分数,每个分数之间恰有一个空格。第三第四行分别表示开始时枪膛内子弹的序列(左边的先打出)和恐怖分子出现的序列(左边的先出现),字母之间都没有任何字符。
每个测试数据之间没有空格和空行。你的程序必须通过全部测试数据,才能被判为AC。
输出
对于每一个测试数据,输出炜炜最多能得到的分数。

Sample Input
3 abc 1 1 1 abc ccc 3 abc 1 1 1 ccc aba

Sample Output
1 0
分析:
这是一个最大公共子序列问题,和最长公共子序列问题类似,字符相同即打中,分数为:dp[i][j]=dp[i][j]=dp[i-1][j-1]+score[i];

字符不同则找上一个状态较大的一个:MAX(dp[i-1][j] ,dp[i][[j-1]);

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<iomanip>
#include<iostream>
#define INF 0x3f3f3f3f
#define MAX 2005
using namespace std;
int dp[MAX][MAX];
int main()
{
	int n,a[405],temp;
	char str[405],ch1[MAX],ch2[MAX];
	int i,j,len1,len2,len;
	while(scanf("%d",&n)!=EOF)
	{
		scanf("%s",str);
		memset(a,0,sizeof(a));
		for(i=0;i<n;i++)
			scanf("%d",&a[str[i]]);
		scanf("%s %s",ch1,ch2);
		len1=strlen(ch1);
		len2=strlen(ch2);
		for(i=1;i<=len1;i++)
		{
			for(j=1;j<=len2;j++)
			{
				if(ch1[i-1]==ch2[j-1])
					dp[i][j]=dp[i-1][j-1]+a[ch1[i-1]];
				else
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
		printf("%d\n",dp[len1][len2]);
	}
	return 0;
}

 给定 n 只老鼠,每只老鼠有体重和速度,求老鼠的一个最长序列,使得体重严格递增,速度严格递减。给出这个序列的长度,并且输出这个序列中的每只老鼠在输入中的序号。

解题思路:先对所有老鼠排序,第一个关键字体重从小到大,第二关键字速度从大到小。然后求这个序列中的最长严格递增子序列。dp[i] 记录以 i 只老鼠作为结尾的最长合法序列的长度,则有状态转移方程 dp[i]=max{dp[j]+1},0<=j<i, 只老鼠前面。
 

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<iomanip>
#include<iostream>
#define INF 0x3f3f3f3f
#define MAX 2005
using namespace std;

struct zc
{
    int w;
    int s;
    int n;
}m[1005];
 
int dp[1005];
 
bool comp(const zc &a,const zc&b)
{
    if(a.w!=b.w)
        return a.w<b.w;
    return a.s>b.s;
} 
int main()
{
	    int i,j,k;
    for(i=0;i<1005;i++)
        dp[i]=1;
 
    for(i=1;cin>>m[i].w>>m[i].s;i++)
        m[i].n=i;
    sort(m+1,m+i,comp);
 
    int maxd=1;
    for(j=2;j<i;j++)
        for(k=1;k<j;k++)
            if(m[k].w < m[j].w && m[k].s > m[j].s)
            {
                dp[j] = max(dp[k]+1, dp[j]); 
                if(dp[j] > dp[maxd])
                    maxd = j;
            }
    cout<<dp[maxd]<<endl;
    int temp=maxd;
    vector<int>mm;
    mm.push_back(maxd);
    for(j=maxd-1;j>=1;j--)
        if(dp[maxd]==dp[j]+1)
        {
            mm.push_back(j);
            maxd=j;
        }
    for(j=dp[temp]-1;j>=0;j--)
        cout<<m[mm[j]].n<<endl;

}

 感悟

做题的速度太慢,因为见的题还是不够多,要加紧步伐多多看多多做。

山东开始陆续分批开学了,估计到我们也得五月中旬,希望疫情赶紧过去,虽说4月4是在昨天,我们永远不能忘记默默付出和奋战的医务工作者,向你们表示敬意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值