文章目录
课程
2602.Bone Collector
题目链接
背包问题,价值+体积
- 贪心算法;
- 动态规划;
- 暴力搜索:
- 0-1背包问题,对应唯一的二进制,设n=4,[0000,0001,0010,0011,…,1111]=2^4,对应唯一的十进制
eg1:
- c++的bitset类,参考
#include <iostream>
#include <bitset>//二进制
#include <cstdio>
using namespace std;
int main(){
int i,j,n,v,T;
int volume[1005],value[1005];
scanf("%d",&T);
while(T--){
cin>>n>>v;
for(i=0;i<n;i++)
{
scanf("%d",value+i);//数组名字时指针,&value[i]
}
for(i=0;i<n;i++)
{
scanf("%d",volume+i);
}
int max_value=-1;
//8421码
//1左移一位->10->2,时间复杂度:O(n)=2^n*n
for(i=0;i<(1<<n);i++)//for(i=0;i<pow(2,n);i++)
{
//bitset<n> Bi(i);//将十进制整数i转成n位二进制
bitset<32> Bi(i);
int temp=i;
int current_volume=0,current_value=0;
for(j=0;j<n;j++)
{
if(Bi.test(j))//测试//if(temp%2==1)
{
current_volume += volume[j];
current_value += value[j];
}
temp /= 2;
}
if(current_volume<=v && current_value>max_value)
{
max_value=current_value;
}
}
cout<<max_value<<endl;
}
return 0;
}
eg2:
动态规划:空间换时间,类似归纳法
4549.M斐波那契数列
#include <iostream>
#include <vector>
using namespace std;
int fib(int a, int b, int N) {
if (N <= 1) return N;
vector<int> dp(N + 1);
dp[0] = a;
dp[1] = b;
for (int i = 2; i <= N; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[N];
}
int main(){
int a,b,n;
cin>>a>>b>>n;
int res = fib(a,b,n);
cout<<res<<endl;
return 0;
}
练习
1231.最大连续子序列
#include <stdio.h> // 动态规划
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct{
int start,end;
int weight; //从i到k的最大子序列长度 状态迁移函数:t[i].weight=t[i+1].weight+arr[i] i在序列中
}xulie;
xulie t[10005];
bool cmp(xulie a,xulie b)
{
return a.weight>b.weight;
}
int main()
{
int k,i,flag=0;
int arr[10005];
// freopen("f:/in.txt","r",stdin);
while(~scanf("%d",&k)&&k)
{
for(i=1;i<=k;i++)
cin>>arr[i];
for(i=1;i<=k;i++)
{
t[i].weight=arr[i]>0?arr[i]:0; //序列 从i到k的最大子序列长度初始化为 arr[i] 与 0 中的较大值
t[i].start=t[i].end=i;
}
for(i=k-1;i>0;i--) //逆推 critical codes
{
if(t[i+1].weight>0)
{
t[i].weight=t[i+1].weight+arr[i];
t[i].start=i;
t[i].end=t[i+1].end;
}
}
sort(t+1,t+k,cmp);
for(i=1;i<=k;i++) //判断是否全是负值
{
if(arr[i]>=0)
break;
}
if(i==k+1)
flag=1;
if(flag)
cout<<0<<" "<<arr[1]<<" "<<arr[k]<<endl; // 全是负值情况输出
else if(t[1].weight!=0)
cout<<t[1].weight<<" "<<arr[t[1].start]<<" "<<arr[t[1].end]<<endl;
else
cout<<0<<" "<<0<<" "<<0<<endl; // 若最大连续序列为0,直接输出0 0 0
flag=0;
}
// fclose(stdin);
return 1;
}
2955.Robberies
动态规划 - 0-1背包问题
被抓到的概率 - 抢到的钱数
背包 - 价值
dp[i][j]=从下标为0-i的物品里任意取,放入容量为j的背包,得到的最大价值
dp[i][j]=
1.不放物品i:=dp[i-1][j]
2.放物品i:=dp[i-1][j-weigtht[i]]+value[i]
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
dp[i][0]=0
dp[0][j]=
当 j < weight[0]时,dp[0][j] = 0,因为背包容量比编号0的物品重量还小。
当 j >= weight[0]时,dp[0][j] = value[0],因为背包容量放足够放编号0物品。
将银行总钱数作为背包容量。
不被抓情况下的最大钱数,即求最大的逃跑概率,所以是(1-p[i])。
从大到小遍历背包容量,概率超过(1-p)即输出。
状态方程:dp[j] = max(dp[j], dp[j - w[i]] * (1 - v[i])); //dp[i]为抢i金币后的总逃跑概率。
显然dp[0]=1;
#include <iostream>
#include <vector>
#include <utility>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <cstdio>
#include <fstream>
#include <set>
using namespace std;
typedef long long ll;
#define MAXN 10005
double dp[MAXN];
double v[MAXN];
int w[MAXN];
int main() {
int T;
cin >> T;
while (T--) {
memset(dp, 0, sizeof(dp));
double m;
int n;
cin >> m >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
sum += w[i];
}
dp[0] = 1;
for (int i = 1; i <= n; i++)
for (int j = sum; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] * (1 - v[i]));
for (int i = sum; i >= 0; i--) {
if (dp[i] > (1 - m)) {
cout << i << endl;
break;
}
}
}
return 0;
}
1864.最大报销额
参考
可在系统通过,但是在codeblocks无法运行。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
int v[100],w[100];
double dp[3000005];
int main()
{
double q;
int n,t;
while(scanf("%lf%d",&q,&n)!=EOF)
{
double price;
char ch;
int j=0,k=0;
if(n==0)
{
break;
}
for(int i=0; i<n; i++)
{
scanf("%d",&t);
int flag=1;
double a=0,b=0,c=0;
while(t--)
{
getchar();
scanf("%c:%lf",&ch,&price);
if(ch=='A')//算出每个发票的类型A物品的总价格
a+=price;
else if(ch=='B')//算出每个发票的类型B物品的总价格
b+=price;
else if(ch=='C')//算出每个发票的类型C物品的总价格
c+=price;
else//如果出现其他类型的物品就把flag标记为0,代表为不能报销的发票
flag=0;
if(a>600||b>600||c>600)//如果A或者B或者C物品的单个价格有一个大于600就说明这个发票不能报销,就标记为0
flag=0;
}
if(flag==1&&(a+b+c)<=1000)//当flag等于1并且A,B,C的总钱数小于等于1000就是一张可以报销的发票
{
v[k]=(a+b+c)*100;//因为题目说了最后的结果是保留两位小数,我们就把所以数都转换为整形来使得计算更加简单,所以就乘以100.
w[k]=(a+b+c)*100;//同上一样的作业
k++;//存储完之后加1,接下来再存储满足条件可报销的情况
}
}
int c=q*100;//这时候总钱数也需要乘以100.
memset(dp,0,sizeof(dp));
for(int i=0; i<k; i++)
{
for(int j=c; j>=w[i]; j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
printf("%.2lf\n",dp[c]/100);
}
}
自己:
#include <iostream>
#include <vector>
#include <utility>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <cstdio>
#include <fstream>
#include <set>
using namespace std;
typedef long long ll;
#define MAXN 10005
int bag(vector<int> weight,vector<int> value,int bagWeight) {
vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0));
for (int j = weight[0]; j <= bagWeight; j++) {
dp[0][j] = value[0];
}
for(int i = 1; i < weight.size(); i++) {
// 遍历物品
for(int j = 0; j <= bagWeight; j++) {
// 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
return dp[weight.size() - 1][bagWeight];
}
int main() {
double w;
int n,m;
while(scanf("%lf %d", &w, &n)!=EOF){
if(n==0) break;
double price;
char type;
int a,b,c;
vector<int> weight,value;
for(int i=0;i<n;i++){
cin>>m;
int flag=1;
while(m--){
scanf("%c:%lf",&type,&price);
if(type=='A') a+=price;
else if(type=='B') b+=price;
else if(type=='c') c+=price;
else flag=0;
}
if(flag==1&&a<=600&&b<=600&&c<=600&&(a+b+c)<=1000){
int sum=(a+b+c)*100;//发票总价值
value.push_back(sum);//v:价值,每张发票的报销价值
weight.push_back(sum);//w:重量,每张发票的报销价值
}
}
w = w*100;//报销总数
int money = bag(weight,value,w);
printf("%.2lf\n",(double)money/100);
}
return 0;
}
/*
200.00 3
2 A:23.50 B:100.00
1 C:650.00
3 A:59.99 A:120.00 X:10.00
123.50
*/
1466.计算直线的交点数
Presentation Error
#include <stdio.h>
int main()
{
int a[21][191] = {
0};
for (int i = 0; i < 21; i++){
a[i][0] = 1;
}
for (int x = 2; x <= 20; x++){
for (int n = 0; n <= x; n++){
for (int j = 0; j <= (n - 1) * n / 2; j++){
if (a[n][j] == 1)
a[x][(x - n) * n + j] = 1;
}
}
}
int n;
while (scanf("%d", &n) != EOF){
for (int i = 0; i <= (n - 1) * n / 2; i++){
if (a[n][i] == 1){
printf("%d ", i);
}
}
printf("\n");
}
return 0;
}
accepted:
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int dp[21][192];
int n,i,j,r;
for(i=0;i<21;i++)//i条直线
{
for(j=0;j<192;j++)//j个交点
{
if(j==0) dp[i][j]=1;//与0条线平行
else dp[i][j]=0;
}
}
for(i=0;i<21;i++)
{
for(r=0;r<i;r++)
{
for(j=0;j<192;j++)
{
if(dp[r][j]==1)
dp[i][(i-r)*r+j]=1;
}
}
}
while(~scanf("%d",&n))
{
for(i=0;i<n*(n-1)/2;i++)
{
if(dp[n][i])
cout<<i<<" ";
}
cout<<i<<endl;
}
return 0;
}
2151.Worm
#include<stdio.h>
#include<string.h>
/*
*dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1]:
*表示第i分钟,第j棵树时的行走方案数;
*初始化:dp[0][p]=1
*/
int dp[105][105];
int main()
{
int n,m,p,t;
int i,j;
while(scanf("%d%d%d%d",&n,&p,&m,&t)!=EOF)
{
memset(dp,0,sizeof(dp));
dp[0][p]=1;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];
}
printf("%d\n",dp[m][t]);
}
return 0;
}
2084. 数塔
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<iostream>
/*
dp[i][j] = 加入第i行第j列,最大的和
dp[i][j] =max(dp[i-1][j],dp[i-1][j-1])+dp[i][j]
dp[0][j]=0
dp[i][0]=0
*/
using namespace std;
int main()
{
int t,h,temp;
scanf("%d",&t);
while(t--){
scanf("%d",&h);
int dp[h+5][h+5];
memset(dp,0,sizeof(dp));
int num[h+5][h+5];
memset(num,0,sizeof(num));
int len=1;
int temp_h=h;
while(temp_h--){
//5
for(int i=1;i<=len;i++){
cin>>temp;
num[len][i]=temp;
}
len++;
}
for(int i=1;i<=h;i++){
for(int j=1;j<=h;j++){
dp[i][j] =max(dp[i-1][j],dp[i-1][j-1])+num[i][j];
}
}
/*
cout<<"num:\n"<<endl;
for(int i=1;i<=h;i++){
for(int j=1;j<=h;j++){
printf("%d ",num[i][j]);
}
cout<<endl;
}
cout<<"dp:\n"<<endl;
for(int i=1;i<=h;i++){
for(int j=1;j<=h;j++){
printf("%d ",dp[i][j]);
if(dp[h][j]>maxnum){
maxnum=dp[h][j];
}
}
cout<<endl;
}*/
int maxnum=0;
for(int j=1;j<=h;j++){
if(dp[h][j]>maxnum){
maxnum=dp[h][j];
}
}
cout<<maxnum<<endl;
}
return 0;
}
2059.龟兔赛跑
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
double dp[200];
#define INF 0xfffffff
int main()
{
double l,c,t,v1,v2,vr,a[100000];
int n;
while(scanf("%lf%d%lf%lf%lf%lf%lf",&l,&n,&c,&t,&vr,&v1,&v2)!=EOF)
{
int i,j;
double s;
double time=0;
for(i=1;i<=n;i++)
{
scanf("%lf",&a[i]);
}
a[0]=0;
a[n+1]=l;
dp[0]=0;
for(i=1;i<=n+1;i++)
{
dp[i]=INF;
for(j=0;j<i;j++)
{
s=a[i]-a[j];
if(c>=s)
time=(s*1.0)/v1;
else
time=((s-c)*1.0)/v2+(c*1.0)/v1;
time+=dp[j];
if(j>0)
time+=t;
dp[i]=min(dp[i],time);
}
}
if(dp[n+1]>(l*1.0)/vr)
printf("Good job,rabbit!\n");
else
printf("What a pity rabbit!\n");
}
}
1069.Monkey and Banana
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 35 * 3;
int d[N], n;
struct Cube
{
int a, b, c;
Cube (int aa = 0, int bb = 0, int cc = 0) : a (aa), b (bb), c (cc) {
}
} cub[N];
int dp (int i)
{
if (d[i] > 0) return d[i];
d[i] = cub[i].c;
for (int j = 1; j <= 3 * n; ++j)
if ( (cub[i].a < cub[j].a && cub[i].b < cub[j].b) || (cub[i].a < cub[j].b && cub[i].b < cub[j].a))
d[i] = max (d[i], dp (j) + cub[i].c);
return d[i];
}
int main()
{
int cas = 0, ans, a, b, c;
while (scanf ("%d", &n), n)
{
memset (d, 0, sizeof (d));
for (int i = ans = 0; i < n; ++i)
{
scanf ("%d%d%d", &a, &b, &c);
cub[3 * i + 1] = Cube (a, b, c);
cub[3 * i + 2] = Cube (a, c, b);
cub[3 * i + 3] = Cube (b, c, a);
}
for (int i = 1; i <= 3 * n; ++i)
ans = max (ans, dp (i));
printf ("Case %d: maximum height = %d\n", ++cas, ans);
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int dp[105];
struct node
{
int l,w,h;
}t[105];//存放箱子
bool cmp(node a,node b)
{
if(a.l!=b.l) return a.l>b.l;
if(a.w!=b.w) return a.w>b.w;
}
int main()
{
int n,m,k,g=0;;
while(cin>>n,n)
{
int a,b,c;
k=0;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>a>>b>>c;
t[++k].l=a;t[k].w=b;t[k].h=c;
if(t[k].l<t[k].w) swap(t[k].l,t[k].w);
t[++k].l=b;t[k].w=c;t[k].h=a;
if(t[k].l<t[k].w) swap(t[k].l,t[k].w);
t[++k].l=c;t[k].w=a;t[k].h=b;
if(t[k].l<t[k].w) swap(t[k].l,t[k].w);
}//箱子的高度有三种放法
t[0].l=1e9,t[0].w=1e9,t[0].h=0;
sort(t+1,t+1+k,cmp);
int maxx=0;
for(int i=1;i<=k;i++)
{
for(int j=0;j<i;j++)
{
if(t[i].l<t[j].l&&t[i].w<t[j].w)
{
dp[i]=max(dp[i],dp[j]+t[i].h);
maxx=max(maxx,dp[i]);
}
}
}//下降子序列
g++;
printf("Case %d: maximum height = %d\n",g,maxx);
}
}
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct Node{
int l;
int w;
int h;
int s;
};
bool cmp(Node a,Node b)
{
return a.s>b.s;
}
int main()
{
int n,i,j,k,x,y,z,nu;
Node in[99];
int dp[500];
nu=1;
while(scanf("%d",&n)!=EOF && n!=0)
{
k=0;
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
scanf("%d %d %d",&x,&y,&z);
k++;
in[k].l=x ,in[k].w=y ,in[k].h=z ,in[k].s=x*y;
k++;
in[k].l=y ,in[k].w=z ,in[k].h=x ,in[k].s=y*z;
k++;
in[k].l=z ,in[k].w=x ,in[k].h=y ,in[k].s=x*z;
}
sort(in+1,in+k+1,cmp);
dp[1]=in[1].h;
for(i=2;i<=k;i++)
{
dp[i]=in[i].h;
for(j=1;j<i;j++)
{
if((in[i].l<in[j].l && in[i].w<in[j].w)||(in[i].w<in[j].l && in[i].l<in[j].w))
{
if(dp[j]+in[i].h>dp[i])
dp[i]=dp[j]+in[i].h;
}
}
}
int ma=0;
for(i=1;i<=k;i++)
if(dp[i]>ma)
ma=dp[i];
printf("Case %d: maximum height = %d\n",nu,ma);
nu++;
}
return 0;
}
1058.Humble Numbers
#include <stdio.h>
#define N 5843
int HN[5850],f2=1,f3=1,f5=1,f7=1;
int min(int a,int b,int c,int d)
{
return a>b?b>c?c>d?d:c:d>b?b:d:c<a?d<c?d:c:d<a?d:a;//求出最小值
}
void count(int HN[])
{
int i;
HN[1]=1;
for(i=2; i<=N; i++)
{
HN[i]=min(HN[f2]*2,HN[f3]*3,HN[f5]*5,HN[f7]*7);//状态转移方程
if(HN[i]==HN[f2]*2) f2++;
if(HN[i]==HN[f3]*3) f3++;
if(HN[i]==HN[f5]*5) f5++;
if(HN[i]==HN[f7]*7) f7++;
}
}
int main()
{
int t;
count(HN);
while(~scanf("%d",&t)&&t!=0)
{
if(t%10==1&&t%100!=11)
printf("The %dst humble number is %d.\n",t,HN[t]);
else if(t%10==2&&t%100!=12)
printf("The %dnd humble number is %d.\n",t,HN[t]);
else if(t%10==3&&t%100!=13)
printf("The %drd humble number is %d.\n",t,HN[t]);
else
printf("The %dth humble number is %d.\n",t,HN[t]);
}
return 0;
}
1159.Common Subsequence
题目前面介绍了一下子序列:一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
题目问题是:找到 X 和 Y 的最大长度共同子序列的长度
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
for (int i = 1; i <= text1.size(); i++) {
for (int j = 1; j <= text2.size(); j++) {
if (text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[text1.size()][text2.size()];
}
int main()
{
string s1,s2;
while(cin>>s1>>s2){
int num = longestCommonSubsequence(s1,s2);
cout<<num<<endl;
}
return 0;
}
1087.Super Jumping! Jumping! Jumping!
题意:求上升序列的最大和/最长递增子序列
#define N 1010
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int k[N],dp[N],n;
int main()
{
while(scanf("%d",&n)&&n)
{
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++)
scanf("%d",&k[i]);
dp[0]=k[0];
for(int i=1; i<n; i++)
{
for(int j=0; j<i; j++)
if(k[i]>k[j])//k[i]>k[j]说明依第j项为队尾的上升序列和第i项可以构成上升序列
dp[i]=max(dp[i],dp[j]+k[i]);
dp[i]=max(dp[i],k[i]);//这个是前j项可能存在负数的情况
}
int ma=-1;
for(int i=0; i<n; i++)
ma=max(ma,dp[i]);
printf("%d\n",ma);
}
return 0;
}
1176.免费馅饼
思维参考:
动态规划、另类数塔;
2084-数塔:求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少
参考
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
const int N= 100008;
int f[N][12]; //position in a time then position in all time
int dp[N][12];
int main(){
int n;
while(~scanf("%d",&n)&&n){
memset(f, 0, sizeof(f));
memset(dp, 0, sizeof(dp));
int x,T,maxT=0,m=0;
while(n--){
scanf("%d %d",&x,&T);
f[T][x]++;
maxT=max(maxT,T);
}
dp[1][4]=f[1][4];
dp[1][5]=f[1][5];
dp[1][6]=f[1][6];
for(int i= 2;i<=maxT;i++){
for(int j= 0;j<=10;j++){
dp[i][j]=max({
dp[i -1][j -1],dp[i -1][j],dp[i -1][j+ 1]})+f[i][j];
m=max(m,dp[i][j]);
}
}
printf( "%d\n",m);
}
}