1.理想收入问题
现在已知楚继光手头有某只股票在过去几年中的价格,他试图据此模拟操作以获得理想收入。所谓理想收入是指在股票交易中,以1元为本金可能获得的最高收入,并且在理想收入中允许有非整数股票买卖。
已知股票在第i天每股价格是 V[ i ]元,1 ≤ i ≤ n,求 n 天后的理想收入。
输入格式
输入的第一行为 n (1 ≤ n ≤ 100 000)。下面有 n 个实数,依次为今后 n 天的股票价格。
这 n 个数可能分布在多行中。
输出格式
对于给定的输入,输出 n 天后的理想输入(精确到 0.0001)。
输入样例
4
4.2 2.6
5.6 10.4
输出样例
4.0000
1 #include<iostream>
2 #include<cstdio>
3 #include<cmath>
4 using namespace std;
5 const int size = 100005;
6
7 double v[size], f[size];
8 int main() {
9 int n;
10 scanf("%d", &n);
11 for(int i = 1;i <= n;i++) {
12 scanf("%lf", &v[i]);
13 f[i] = 0;
14 }
15 /* f[i] = max{f[j] / v[k] * v[i]} (1 <= j < i, j < k <= i, f[0] = 1) */
16 f[0] = 1;
17 for(int i = 1;i <= n;i++)
18 for(int j = 0;j < i;j++)
19 for(int k = j + 1;k <= i;k++) {
20 f[i] = max(f[i], f[j] / v[k] * v[i]);
21 }
22 printf("%.4lf\n", f[n]);
23 return 0;
24 }
//朴素动态规划
2.数的划分问题
//f(i,j)表示把i分为j份数目
#include<iostream>
#define MAXSIZE 201
using namespace std;
int f[MAXSIZE][8],n,kind;
int main(){
cin>>n>>kind;
f[1][1] = 1;
for(int i = 2;i<=n;i++)
for(int j=1;j<=kind;j++)
if(i>=j)
f[i][j] = f[i-1][j-1]+f[i-j][j];
cout<<f[n][kind]<<endl;
return 0;
}
其他类数的划分问题(很重要)
数的划分各类问题(divide)
3.硬币问题(hdu2844)
给n种硬币,面值Ai,数量Ci,能凑出多少种不大于m的面值。
样例输入:n m A C
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
样例输出:
8
4
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,a[105],c[105];
bool dp[100005]; //dp[i]表示价格i可以表示出来
int ans[100005]; //ans[i]表示需要硬币数目
int main()
{
while(scanf("%d%d",&n,&m)!=EOF&&(n!=0&&m!=0))
{
memset(dp,false,sizeof(dp));
for(int i=0; i<n; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<n; i++)
{
scanf("%d",&c[i]);
}
dp[0]=true;
for(int i=0; i<n; i++)
{
memset(ans,0,sizeof(ans)); //计数置零;
for(int j=a[i]; j<=m; j++)
{
if(dp[j-a[i]]&&!dp[j]&&ans[j-a[i]]<c[i]) //把数量放在外边,优化了一个for循环;
{
dp[j]=true;
ans[j]=ans[j-a[i]]+1;
}
}
}
int sum=0;
for(int i=m; i>0; i--)
{
if(dp[i])
{
sum++;
}
}
printf("%d\n",sum);
}
return 0;
}
4.唱片录制问题
n首歌中取,发行m张唱片,每张唱片最多t分钟
唱片中的歌曲必须按创作顺序排序
m张唱片包含的歌曲越多越好
输入:
n m t
time1 time2 time3 …
输出
包含最多的歌曲数目
思路
//f[i,j]:前i首歌录了j首所需的最少唱片数
//g[i,j]:前i首歌录了j首当前唱片使用了多少容量
f[i,j]=min(f[i-1,j-1]+1,f[i-1,j])
如果第i首歌要录并且能录进当前唱片里
f[i,j]= f[i-1,j-1]
如果录不进,另开一张唱片
f[i,j]= f[i-1,j-1]
如果第i首歌不录,并且使用的唱片数小于第i首歌录进去的唱片数,自然不录,如果相等,选择当前唱片使用容量最少的方案,这样以后才能录更多。这里记得要保证i!=j(否则就变成前i-1首歌录了i首)。
#include <cstdio>
int n,m,c,a[1005],f[1005][1005],g[1005][1005];
const int inf=2147483647/4;
int main()
{
int i,j;
scanf("%d%d%d",&n,&m,&c);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
f[i][j]=inf;
for (i=1;i<=n;i++)
for (j=1;j<=i;j++)
{
if (g[i-1][j-1]+a[i]<=c)
f[i][j]=f[i-1][j-1],
g[i][j]=g[i-1][j-1]+a[i];
else if (a[i]<=c)
f[i][j]=f[i-1][j-1]+1,
g[i][j]=a[i];
if (i!=j && ((f[i-1][j]<f[i][j]) || (f[i-1][j]==f[i][j] && g[i-1][j]<g[i][j])))
f[i][j]=f[i-1][j],
g[i][j]=g[i-1][j];
}
for (i=n;i>=1;i--)
if (f[n][i]<m)
break;
printf("%d",i);
return 0;
}
5.双色马
N匹马,K个马厩,每一个马都只会是0或1,每一个马厩里会有一个不快乐值(不快乐值=0马的个数*1马的个数),问怎么分配会得出一个最小的不快乐值,输出最小的不快乐值。
#include<bits/stdc++.h>
using namespace std;
#define T 9999999
int main(){
int f[501][501],a[501],s[501];
//f[i][j] : i house,j horses 's min value
//s[i]:to i's white horses' number
//a[i]: record the 1 0 0 1
int i,j,k,n,d;
int Min;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
f[0][i] = 0;
f[i][0] = T;
f[0][0] = 0;
}
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
if(a[i]==1)
s[i] = s[i-1]+1;
else
s[i] = s[i-1];
}
for(int j=1;j<=k;j++)
for(int i=1;i<=n;i++)
{
Min = T;
for(d=0;d<i;d++)
if(Min>f[d][j-1]+(s[i]-s[d])*(i-d-(s[i]-s[d])))
{
Min = f[d][j-1] + (s[i]-s[d])*(i-d-(s[i]-s[d]));
f[i][j] = Min;
}
}
cout<<Min<<endl;
return 0;
}
6.选数统计问题
1至M中选出n个数:A1-An
要求每个数字至少为其前一项的两倍
例如n=4,M=10:
1 2 4 8
1 2 4 9
1 2 4 10
1 2 5 10
问一共可以选出多少不同的数列
分析:显然是构造dp,dp[i][j]表示长度为i,以j结尾的合法方案数。
则dp[i][j]=sum(dp[i-1][k]) k>0&&k<j&&j>2*k
代码:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int dp[15][505]={0};
int n,m;
cin>>n>>m;
//dp[i][j]==dp[i-1][k] if(j==0&&j/k=2)
int i,j,k;
for(i=1;i<=m;i++)
dp[1][i]=1;
for(i=2;i<=n;i++)
for(j=1;j<=m;j++)
for(k=1;k<=j;k++)
if(j>=2*k)
dp[i][j]+=dp[i-1][k];
int ans=0;
for(i=1;i<=m;i++)
ans+=dp[n][i];
cout<<ans<<endl;
return 0;
}
7.最大连续子序列和,k个最大连续子序列和(VIP)
我们用dp[n]表示以第n个数结尾的最大连续子序列的和,于是存在以下递推公式:
dp[n] = max(0, dp[n-1]) + num[n]
仔细思考后不难发现这个递推公式是正确的,则整个问题的答案是max(dp[m]) | m∈[1, N]
#include <stdio.h>
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N, num[134217728];
int main()
{
//输入数据
scanf("%d", &N);
for(int i = 1; i <= N; i++)
scanf("%d", &num[i]);
num[0] = 0;
int ans = num[1];
for(int i = 1; i <= N; i++) {
if(num[i - 1] > 0) num[i] += num[i - 1];
else num[i] += 0;
if(num[i] > ans) ans = num[i];
}
printf("%d\n", ans);
return 0;
}
k个最大连续子序列和,子序列不相交
例如
n=10,k=2
-1 1 -2 3 4 -2 -5 5 6 7
那么:
3 4
5 6 7
是两个连续子序列
7+18 =
25
解决思路:
f[i][j]:数列前j个元素中,i个无公共元素的子序列的最大和
且子序列必须包含第j个元素
状态转移方程:
f[i][j] = max{f[i][j-1]+a[j] , f[i-1][k]+a[j]}
i-1=<k<=j-1
代码:
自己写
8.最长公共上升子序列问题
直接阅读
11.机器分配问题
值得一看
12.系统可靠性
可以一看
13.邮局问题(VIP+PKU1160)
PKU1160
14.调度问题
作为练习
题目源
15.zipper
作为练习
POJ2192
题目源