前言:
话说DP这种纯考思维的题目,总是让我很伤脑筋,一些特别简单的DP我都常常做不出来,所以革命从现在(2018-05-01)开始,努力多刷点DP的练习~。
1.顺序对齐(align)
时间:2018-05-01
Description
考虑两个字符串右对齐的最佳解法。例如,有一个右对齐方案中字符串是AADDEFGGHC 和 ADCDEGH 。
AAD_DEFGGHC
ADCDE__GH_
每一个数值匹配的位置值 +2分,一段连续的空格值 -1 分。所以总分是匹配点的 2 倍减去连续空格的段数,在上述给定的例子中, 6 个位置(A,D,D,E, G,H )匹配,三段空格,所以得分2*6+(-1)*3=9 ,注意,我们并不处罚左边的不匹配位置。若匹配的位置是两个不同的字符,则既不得分也不失分。请你写个程序找出最佳右对齐方案。
Input
输入文件包含两行,每行一个字符串,最长50 个字符。字符全部是大字字母。
Output
一行,为最佳对齐的得分。
Sample Input
AADDEFGGHC
ADCDEGH
Sample Output
9
Solution:
由题意,归纳出以下信息:串$s1$和串$s2$的每一次匹配,只有$4$种情况:两个字符相同得$2$分、两个字符不同$s1$用空格匹配$-1$分、两个字符不同$s2$用空格匹配$-1$分、两个字符不同也不加空格得$0$分。
于是我们不难定义状态$f[i][j]$表示匹配到$s1$串的第$i$个位置和$s2$串的第$j$个位置时能得到的最大分数,则目标状态$f[l1][l2]$($l1=|s1|,l2=|s2|$)。
那么从上面的分析中不难得到状态转移方程:
$f[i][j]=$$\left\{\begin{matrix}
max(f[i][j],f[i-1][j-1]+2)\quad if(s1[i]==s2[j]) \\
max(f[i][j],f[k][j]-1)\quad k\in[1,i) \\
max(f[i][j],f[i][k]-1)\quad k\in[1,j) \\
max(f[i][j],f[i-1][j-1])\quad if(s1[i]!=s2[j]) \\
\end{matrix}\right.$
时间复杂度$O(n^3),n\leq 50$,稳过。
代码:
#include<bits/stdc++.h>
#define il inline
#define Max(a,b) (a)>(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=;
char s1[N],s2[N];
int f[N][N],l1,l2;
int main(){
scanf("%s%s",s1+,s2+);
l1=strlen(s1+),l2=strlen(s2+);
For(i,,l1) For(j,,l2) {
if(s1[i]==s2[j])f[i][j]=Max(f[i][j],f[i-][j-]+);
For(k,,i-)f[i][j]=Max(f[i][j],f[k][j]-);
For(k,,j-)f[i][j]=Max(f[i][j],f[i][k]-);
f[i][j]=Max(f[i][j],f[i-][j-]);
}
cout<<f[l1][l2];
return ;
}
2、任务安排(batch)
时间:2018-05-01
Description
N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。
Input
第一行是N(1<=N<=5000)。
第二行是S(0<=S<=50)。
下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。
Output
一个数,最小的总费用。
Sample Input
5
1
1 3
3 2
4 3
2 3
1 4
153
Solution:
分析本题,可以发现,每多一批任务,等价于使后面的每个机器费用增加对应的费用系数乘上$s$,而每一批任务的总时间应该等于这批任务本来的结束时间(即当前为位置时间$t$的前缀和)加上前面的几批任务增加的时间$s$,该批的总花费就是当前总时间乘这一段的费用系数之和。
于是定义状态$f[i]$表示到了第$i$个任务时的最小费用,初始状态$f[0]=0$(由于要最小费用,其它的$f[i]$初始化为$inf$),用$t[i]$表示到了第$i$个任务的时间前缀,$c[i]$表示到了第$i$个任务的费用系数前缀。
不难得到状态转移方程:$f[i]=min(f[i],f[j]+t[i]*(c[i]-c[j])+s*(c[n]-c[j]))$,表示当前以第$i$个任务为新的一批的末尾,枚举上一批的结束位置$j$(当前这批的开始位置为$j+1$),则该批任务$j+1$到$i$的花费为上一批的任务花费$f[j]$加上新的这一批任务本来的花费$t[i]*(c[i]-c[j])$和增加的$s$的花费$s*(c[n]-c[j])$(注意增加的花费的费用系数和是$c[n]-c[j]$,因为多一批任务后面的每个任务所需时间都会加$s$)。
最后输出目标状态$f[n]$就$ok$了。
代码:
#include<bits/stdc++.h>
#define il inline
#define Min(a,b) (a)>(b)?(b):(a)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=;
int n,s,t[N],c[N],f[N];
int main(){
ios::sync_with_stdio();
cin>>n>>s;
memset(f,0x3f,sizeof(f));f[]=;
For(i,,n)cin>>t[i]>>c[i],t[i]+=t[i-],c[i]+=c[i-];
For(i,,n) For(j,,i-) f[i]=Min(f[i],f[j]+t[i]*(c[i]-c[j])+s*(c[n]-c[j]));
cout<<f[n];
return ;
}
3、最大的算式(bigexp)
时间:2018-05-01
Description
给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果尽量大。因为乘号和加号一共就是N-1个了,所以恰好每两个相邻数字之间都有一个符号。例如:
N=5, K=2,5个数字分别为1、2、3、4、5,可以加成:
1*2*(3+4+5)=24
1*(2+3)*(4+5)=45
(1*2+3)*(4+5)=45
……
Input
输入文件共有二行,第一行为两个有空格隔开的整数,表示N和K,其中(2<=N<=15, 0<=K<=N-1)。第二行为 N个用空格隔开的数字(每个数字在0到9之间)。
Output
输出文件仅一行包含一个整数,表示要求的最大的结果
最后的结果<=maxlongint
Sample Input
5 2
1 2 3 4 5
120
Solution:
本题定义状态$f[i][j]$表示前$i$个数用了$j$个乘号的最大运算值(开始时前缀和处理,默认加法,等同于把加号变成乘号再左右两边加括号)。
那么初始状态$f[i][0]=\sum{a[k]},k\in[1,i]$,目标状态$f[n][k]$,不难想出状态转移方程:$f[i][j]=max(f[i][j],f[p][j-1]*(f[i][0]-f[p][0])),\;p\in[1,i)$。
代码:
#include<bits/stdc++.h>
#define Max(a,b) (a)>(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long ll;
const int N=;
ll n,k,f[N][N],a[N];
int main(){
ios::sync_with_stdio();
cin>>n>>k;
For(i,,n)cin>>a[i],a[i]+=a[i-],f[i][]=a[i];
For(i,,n) For(j,,i-) For(p,,i-)
f[i][j]=Max(f[i][j],f[p][j-]*(f[i][]-f[p][]));
cout<<f[n][k];
return ;
}
4、字符串距离(blast)
时间:2018-05-02
Description
设有字符串X,我们称在X的头尾及中间插入任意多个空格后构成的新字符串为X的扩展串,如字符串X为”abcbcd”,则字符串“abcb□cd”,“□a□bcbcd□”和“abcb□cd□”都是X的扩展串,这里“□”代表空格字符。
如果A1是字符串A的扩展串,B1是字符串B的扩展串,A1与B1具有相同的长度,那么我扪定义字符串A1与B1的距离为相应位置上的字符的距离总和,而两个非空格字符的距离定义为它们的ASCII码的差的绝对值,而空格字符与其他任意字符之间的距离为已知的定值K,空格字符与空格字符的距离为0。在字符串A、B的所有扩展串中,必定存在两个等长的扩展串A1、B1,使得A1与B1之间的距离达到最小,我们将这一距离定义为字符串A、B的距离。
请你写一个程序,求出字符串A、B的距离。
Input
输入文件第一行为字符串A,第二行为字符串B。A、B均由小写字母组成且长度均不超过2000。第三行为一个整数K(1≤K≤100),表示空格与其他字符的距离。
Output
输出文件仅一行包含一个整数,表示所求得字符串A、B的距离。
Sample Input
cmc
snmn
2
10
Solution:
本题一道普通$DP$。
设$f[i][j]$表示匹配到$s1$的$i$位置和$s2$的$j$位置时的最小距离,初始状态$f[i][0]=f[i-1]+k$(表示$s1$第$i$个字符和空格匹配),同理$f[0][j]=f[0][j-1]+k$,目标状态$f[|s1|][|s2|]$。
由题意易得,每次匹配只有三种情况:不加空格直接匹配$i,j$,$i$加空格即$i-1,j$匹配的基础上加$k$,$j$加空格即$i,j-1$匹配的基础上加$k$,取三者最小值即可。
则状态转移方程:$f[i][j]=min(f[i-1][j-1]+abs(s1[i]-s1[j]),min(f[i][j-1]+k,f[i-1][j]+k))$
代码:
#include<bits/stdc++.h>
#define il inline
#define Min(a,b) (a)<(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=;
int l1,l2,k,f[N][N];
char s1[N],s2[N];
int main(){
scanf("%s%s%d",s1+,s2+,&k);
l1=strlen(s1+),l2=strlen(s2+);
memset(f,0x3f,sizeof(f));f[][]=;
For(i,,l1)f[i][]=f[i-][]+k;
For(i,,l2)f[][i]=f[][i-]+k;
For(i,,l1) For(j,,l2)
f[i][j]=Min(f[i-][j-]+abs(s1[i]-s2[j]),Min(f[i-][j]+k,f[i][j-]+k));
cout<<f[l1][l2];
return ;
}
5、书的复制(Book)
时间:2018-05-02
Description
现在要把m本有顺序的书分给k给人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。
现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
Input
第一行两个整数m,k;(k≤m≤500)
第二行m个整数,第i个整数表示第i本书的页数。
Output
共k行,每行两个整数,第i行表示第i个人抄写的书的起始编号和终止编号。k行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。
Sample Input
9 3
1 2 3 4 5 6 7 8 9
1 5
6 7
8 9
Solution:
虽说这题名义上是$DP$,但是我还是无脑二分做了。直接二分答案,然后贪心判断,输出时直接递归输出就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
int m,k,l,r,mid,a[];
inline bool check(int x)
{
int tot=,num=;
for(int i=m;i>=;i--)
{
if(tot+a[i]<=x)tot+=a[i];
else tot=a[i],num++;
}
return num>=k;
}
inline void print(int m,int k,int x)
{
int s,q;
if(k==)return;
if(k==){printf("1 %d\n",m);return;}
s=,q=m;
while(s+a[q]<=x){s+=a[q];q--;}
print(q,k-,x);
printf("%d %d\n",q+,m);
}
int main()
{
scanf("%d%d",&m,&k);
for(int i=;i<=m;i++)scanf("%d",&a[i]),r+=a[i];
while(l<=r)
{
mid=l+r>>;
if(check(mid))l=mid+;
else r=mid-;
}
print(m,k,l);
return ;
}
6、公路乘车(busses)
时间:2018-05-03
Description
一个特别的单行街道在每公里处有一个汽车站。顾客根据他们乘坐汽车的公里使来付费。例如样例的第一行就是一个费用的单子。
没有一辆车子行驶超过10公里,一个顾客打算行驶n公里(1<=n<=100),它可以通过无限次的换车来完成旅程。最后要求费用最少。
Input
第一行十个整数分别表示行走1到10公里的费用(<=500)。注意这些数并无实际的经济意义,即行驶10公里费用可能比行驶一公里少。
第二行一个整数n表示,旅客的总路程数。
Output
仅一个整数表示最少费用。
Sample Input
12 21 31 40 49 5869 79 90 101
15
Sample Output
147
Solution:
本题,直接是裸的完全背包问题,$f[i]$表示走$i$距离的最少花费,$f[i]=min(f[i],f[i-j]+w[j])$(注意枚举顺序),输出目标状态$f[n]$就$ok$了。
代码:
#include<bits/stdc++.h>
#define Min(a,b) (a)>(b)?(b):(a)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=;
int w[],n,f[N];
int main(){
ios::sync_with_stdio();
memset(f,0x3f,sizeof(f));f[]=;
For(i,,)cin>>w[i];cin>>n;
For(i,,) For(j,i,n) f[j]=Min(f[j],f[j-i]+w[i]);
cout<<f[n];
return ;
}
7. 积木城堡(Castle)
时间:2018-05-03
Description
XC的儿子小XC最喜欢玩的游戏用积木垒漂亮的城堡。城堡是用一些立方体的积木垒成的,城堡的每一层是一块积木。小XC是一个比他爸爸XC还聪明的孩子,他发现垒城堡的时候,如果下面的积木比上面的积木大,那么城堡便不容易倒。所以他在垒城堡的时候总是遵循这样的规则。
小XC想把自己垒的城堡送给幼儿园里漂亮的女孩子们,这样可以增加他的好感度。为了公平起见,他决定把送给每个女孩子一样高的城堡,这样可以避免女孩子们为了获得更漂亮的城堡而引起争执。可是他发现自己在垒城堡的时候并没有预先考虑到这一点。所以他现在要改造城堡。由于他没有多余的积木了,他灵机一动,想出了一个巧妙的改造方案。他决定从每一个城堡中挪去一些积木,使得最终每座城堡都一样高。为了使他的城堡更雄伟,他觉得应该使最后的城堡都尽可能的高。
任务:
请你帮助小XC编一个程序,根据他垒的所有城堡的信息,决定应该移去哪些积木才能获得最佳的效果。
Input
第一行是一个整数N(N<=100),表示一共有几座城堡。以下N行每行是一系列非负整数,用一个空格分隔,按从下往上的顺序依次给出一座城堡中所有积木的棱长。用-1结束。一座城堡中的积木不超过100块,每块积木的棱长不超过100。
Output
一个整数,表示最后城堡的最大可能的高度。如果找不到合适的方案,则输出0。
Sample Input
2
2 1 –1
3 2 1 –1
3
Solution:
本题,开始时还看错题了,以为是求最长上升子序列(还调了2次,手动滑稽~)。
那么一句话讲下本题题意,可以理解为就是$n$个背包,要求每个背包装出相同容量,求这个最大容量。
于是每次跑一遍完全背包,然后记录一下能装出的容量。最后输出次数出现$n$次的最大容量。
代码:
#include<bits/stdc++.h>
#define Max(a,b) (a)>(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
int n,a[N],f[N],ans,m,c,t,q,s[N]; int main(){
ios::sync_with_stdio();
cin>>n;q=n;
while(n--){
m=;t=;
while(cin>>c&&c!=-){
m++;a[m]=c;t+=c;
}
memset(f,,sizeof(f));
f[]=;
For(i,,m) Bor(j,a[i],t)if(f[j-a[i]]&&!f[j])f[j]=f[j-a[i]],s[j]++;
}
Bor(i,,)if(s[i]==q){ans=i;break;}
cout<<ans;
return ;
}
8、筷子(chop)
时间:2018-05-03
Description
A先生有很多双筷子。确切的说应该是很多根,因为筷子的长度不一,很难判断出哪两根是一双的。这天,A先生家里来了K个客人,A先生留下他们吃晚饭。加上A先生,A夫人和他们的孩子小A,共K+3个人。每人需要用一双筷子。A先生只好清理了一下筷子,共N根,长度为T1,T2,T3,……,TN.现在他想用这些筷子组合成K+3双,使每双的筷子长度差的平方和最小。(怎么不是和最小??这要去问A先生了,呵呵)
Input
输入文件共有两行,第一行为两个用空格隔开的整数,表示N,K(1≤N≤100, 0<K<50),第二行共有N个用空格隔开的整数,为Ti.每个整数为1~50之间的数。
Output
输出文件仅一行。如果凑不齐K+3双,输出-1,否则输出长度差平方和的最小值。
Sample Input
10 1
1 1 2 3 3 3 4 6 10 20
Sample Output
5
Solution:
本题首先贪心的想到,排序,然后相邻两个组合肯定比用其中一个和其它的组和要好,但是由于只要选$k$对,所以我们并不知道怎么选最优。
于是在贪心的基础上进行$DP$,先排序,用$s[i]$表示第$i$根和第$i+1$根搭配的长度差平方,然后定义状态$f[i][j]$表示前$i$根中选了$j$对的长度差平方和的最小值,则每次转移$f[i][j]$时,只有两种情况:
1、第$i-1$根不与第$i$根搭配,则在前$i-1$根中搭配$j$对,即$f[i][j]=f[i-1][j]$;
2、第$i-1$根和第$i$根搭配,则$s[i-1]$加上前$i-2$根搭配$j-1$对的值,即$f[i][j]=f[i-2][j-1]+s[i-1]$。
所以状态转移方程:$f[i][j]=min(f[i-1][j],f[i-2][j-1]+s[i-1])$。输出目标状态$f[n][k+3]$就$ok$了。
代码:
#include<bits/stdc++.h>
#define Min(a,b) (a)>(b)?(b):(a)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=;
int n,k,t[N],f[N][N],s[N];
int main(){
ios::sync_with_stdio();
cin>>n>>k;k+=;
For(i,,n)cin>>t[i];
sort(t+,t+n+);
For(i,,n) s[i]=(t[i]-t[i+])*(t[i]-t[i+]);
memset(f,0x3f,sizeof(f));
For(i,,n)f[i][]=;
For(i,,n) For(j,,k)f[i][j]=Min(f[i-][j],f[i-][j-]+s[i-]);
cout<<f[n][k];
return ;
}
9、护卫队(Convoy)
时间:2018-05-04
Description
护卫车队在一条单行的街道前排成一队,前面河上是一座单行的桥。因为街道是一条单行道,所以任何车辆都不能超车。桥能承受一个给定的最大承载量。为了控制桥上的交通,桥两边各站一个指挥员。护卫车队被分成几个组,每组中的车辆都能同时通过该桥。当一组车队达到了桥的另一端,该端的指挥员就用电话通知另一端的指挥员,这样下一组车队才能开始通过该桥。每辆车的重量是已知的。任何一组车队的重量之和不能超过桥的最大承重量。被分在同一组的每一辆车都以其最快的速度通过该桥。一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的。问题要求计算出全部护卫车队通过该桥所需的最短时间值。
Input
输入文件第一行包含三个正整数(用空格隔开),第一个整数表示该桥所能承受的最大载重量(用吨表示);第二个整数表示该桥长度(用千米表示);第三个整数表示该护卫队中车辆的总数(n<1000)。接下来的几行中,每行包含两个正整数W和S(用空格隔开),W表示该车的重量(用吨表示),S表示该车过桥能达到的最快速度(用千米/小时表示)。车子的重量和速度是按车子排队等候时的顺序给出的。
Output
输出文件应该是一个实数,四舍五入精确到小数点后1位,表示整个护卫车队通过该桥所需的最短时间(用分钟表示)。
Sample Input
100 5 10
40 25
50 20
50 20
70 10
12 50
9 70
49 30
38 25
27 50
19 70
75.0
Solution:
我*~这题贼坑,提醒要开$long\;long$,然后就是初始化赋值要注意。。。(我错了几遍都是因为这个)
首先,不难发现此题和任务安排那道$DP$类似。于是仿照那道题,定义状态$f[i]$表示到了前$i$辆车都通过桥的最少时间,则目标状态$f[n]$。
转移时先赋初始状态$f[i]=f[i-1]+c/s[i]$($c$为长度,$s[i]$表示速度),注意一定要在循环中赋初值,因为每次$f[i-1]$的值会被更新。从后往前枚举当前$i$为结尾的一组的起点$j$(从后往前是为了便于记录$s[i]$即速度的最小值,也很好判断限制重量的条件,即时退出循环),不难得到状态转移方程:$f[i]=min(f[i],f[j-1]+)+\frac{c}{p}$(这不需要解释了)。
代码:
#include<bits/stdc++.h>
#define Min(a,b) (a)>(b)?(b):(a)
#define Max(a,b) (a)>(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
long long n,m,c,w[N];
double s[N],f[N],p; int main(){
cin>>m>>c>>n;
For(i,,n)cin>>w[i]>>s[i],w[i]=w[i]+w[i-];
For(i,,n){
p=s[i];f[i]=f[i-]+c/s[i];
Bor(j,,i-){
p=Min(p,s[j]);
if(w[i]-w[j-]>m)break;
f[i]=Min(f[i],f[j-]+c/p);
}
}
printf("%.1lf",f[n]*);
return ;
}
10、对话(dialog)
时间:
Description
从前有两个人,一个名为"one",另一个则叫"puton"。很奇怪,"one"除了称呼"puton"名字外,只对他说"out"和"output"两个单词;"puton"除了称呼"one"名字外,只对他说"in"和"input"两个单词。
最近人们在研究他们对话,但是,由于资料的混乱,其中可能有一些不是他们的对话。你的任务是鉴别一些句子,判断这些句子是否可能是他们的对话。(即,判断句子是否可以被划分成若干单词,这些单词只可以是"one"、"puton"、"out"、"output"、"in"和"input")。
输入n个字符串,长度不超过200,表示一句句子。如果可能是那两个人的对话,则输出”YES”;否则,输出”NO”。
Input
第一行一个整数n,表示一共有n句句子。
此后每行一个字符串,表示一句句子。
Output
n行,每行一个”YES”或”NO”,表示你的判断结果。
Sample Input
6
puton
inonputin
oneputonininputoutoutput
oneininputwooutoutput
outpu
utput
Sample Output
YES
NO
YES
NO
NO
NO
Solution:
然而我还没调出来(手动滑稽~)
11、DOLLARS(dollars)
时间:2018-05-06
Description
在以后的若干天里戴维将学习美元与德国马克的汇率。编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值。
Input
输入文件的第一行是一个自然数N,1≤N≤100,表示戴维学习汇率的天数。
接下来的N行中每行是一个自然数A,1≤A≤1000。第i+1行的A表示预先知道的第i+1天的平均汇率,在这一天中,戴维既能用100美元买A马克也能用A马克购买100美元。
Output
输出文件的第一行也是唯一的一行应输出要求的钱数(单位为美元,保留两位小数)。
注意:考虑到实数算术运算中进位的误差,结果在正确结果0.05美元范围内的被认为是正确的,戴维必须在最后一天结束之前将他的钱都换成美元。
Sample Input
5
400
300
500
300
250
266.67
样例解释 (无需输出)
Day 1 ... changing 100.0000 美元= 400.0000 马克
Day 2 ... changing 400.0000 马克= 133.3333 美元
Day 3 ... changing 133.3333 美元= 666.6666 马克
Day 5 ... changing 666.6666 马克= 266.6666 美元
update:2017/08/18: 1样例正确答案为266.67
2已经把测试点1改成样例
3spj原先误差是0.01,按照题目要求,重新改为0.05
Solution:
本题太水,直接贪心模拟就$OK$了。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[];double d=,m;
int main()
{
cin>>n;
for(int i=;i<=n;i++)cin>>a[i];
for(int i=;i<=n;i++)
{
d=max(d,m*/a[i]);
m=max(m,d*a[i]/);
}
printf("%.2lf",d);
return ;
}
12、最大约数和(max)
时间:2018-06-20
Description
选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。
Input
输入一个正整数S。
Output
输出最大的约数之和。
Sample Input
11
9
说明
样例说明
取数字4和6,可以得到最大值(1+2)+(1+2+3)=9。
数据规模
S<=1000
Solution:
本题数据较小。
考虑预处理$1\rightarrow s$每个数的除开本身的约数和。
然后就把每个数的大小当做体积,约数和当做价值,跑一下$0/1$背包就好了。
代码:
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)>(b)?(b):(a))
using namespace std;
const int N=;
int s,v[N],f[N],ans; int main(){
ios::sync_with_stdio();
cin>>s;
For(i,,s) For(j,,i>>) if(i%j==) v[i]+=j;
For(i,,s) Bor(j,,i) f[i]=Max(f[i],f[i-j]+v[j]);
For(i,,s) ans=Max(ans,f[i]);
cout<<ans;
return ;
}
13、A+B Problem(and)
时间:2018-06-27
Description
·1+1=? 显然是2
·a+b=? 1001回看不谢
·哥德巴赫猜想 似乎已呈泛滥趋势
·以上纯属个人吐槽
·给定一个正整数n,求将其分解成若干个素数之和的方案总数。
Input
一行:一个正整数n
Output
一行:一个整数表示方案总数
Sample Input
7
3
说明
【样例解释】
7=7 7=2+5
7=2+2+3
【福利数据】
【输入】 20
【输出】 26
【数据范围及约定】
对于30%的数据 1<=n<=10
对于100%的数据,1<=n<=10^3
Solution:
本题数据比较小?(答案竟然爆int)。
首先,我们先筛出$1000$以内的素数,然后因为是要求某一个数用素数相加形式的方案数,每个素数可以选多次也可以不选,所以考虑写DP。
那么直接套上完全背包的思路就好了。
注意最后答案会爆int,所以要开long long。
代码:
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
ll n,prime[N+],cnt,f[N];
bool isprime[N+]; int main(){
ios::sync_with_stdio();
cin>>n;
For(i,,n){
if(!isprime[i]) prime[++cnt]=i;
for(int j=;j<=cnt&&prime[j]*i<=n;j++){
isprime[prime[j]*i]=;
if(i%prime[j]==)break;
}
}
f[]=;
For(i,,cnt) For(j,prime[i],n) f[j]+=f[j-prime[i]];
cout<<f[n];
return ;
}