上午复习了背包问题。
01背包问题
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
4 5
1 2
2 4
3 4
4 5
8
背包问题,将每个物品分成取或者不取的情况,然后判断取或不取那个比较大,然后取了那个物品,就把空间减去那个物品的空间,价值加上那个物品的价值。
核心代码:
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
v[i]为第i件物品的体积,w[i]为价格。
代码实现:
#include<stdio.h>
const int N=1000;
int n,m;
int v[N],w[N];
int f[N];
int max(int a,int b)
{
int z;
z=a>b?a:b;
return (z);
}
int main()
{
int max(int a,int b);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d %d",&v[i],&w[i]);
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
printf("%d",f[m]);
}
完全背包
与01背包不同的是完全背包中的物品可以多次选择,而01背包中的物品只能选择一次。
两者代码的区别在这:
核心代码:
for(int i=0;i<n;i++)
{
for(int j=v[i];j<=m;j++) //背包与完全背包的区别
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
//背包与完全背包的区别
01背包 高->低 (每次选择选这件物品或者下一次选择这件物品,取决于那个更大)
// f[5]=f[5] || f[4]+w[1];(取最大值)
// f[4]=f[4] || f[3]+w[1];
// f[3]=f[3] || f[2]+w[1];
// f[2]=f[2] || f[1]+w[1];
// f[1]=f[0]+w[1];
// f[5]=f[5] || f[3]+w[2];
// f[4]=f[4] || f[2]+w[2];
// f[3]=f[3] || f[1]+w[2];
// f[2]=f[2] || f[0]+w[2];
完全背包 低->高(每次选择都可能包含相同的物品)
// f[1]=f[0]+w[1];(取最大值)
//f[2]=f[2] || f[1]+w[1];
//f[3]=f[3] || f[2]+w[1];
//f[4]=f[4] || f[3]+w[1];
//f[5]=f[5] || f[4]+w[1];
//f[2]=f[2] || f[0]+w[2];
//f[3]=f[3] || f[1]+w[2];
//f[4]=f[4] || f[2]+w[2];
代码实现:
#include<stdio.h>
int v[1000],w[1000];
int f[1000];
int max(int a,int b)
{
int z;
z=a>b?a:b;
return (z);
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d %d",&v[i],&w[i]);
for(int i=0;i<n;i++)
{
for(int j=v[i];j<=m;j++)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
printf("%d",f[m]);
}
P1455 搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 nn 朵云,云朵已经被老板编号为 1,2,3,...,n1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。
输入格式
第一行输入三个整数,n,m,wn,m,w,表示有 nn 朵云,mm 个搭配和你现有的钱的数目。
第二行至 n+1n+1 行,每行有两个整数, c_i,d_ici,di,表示第 ii 朵云的价钱和价值。
第 n+2n+2 至 n+1+mn+1+m 行 ,每行有两个整数 u_i,v_iui,vi。表示买第 u_iui 朵云就必须买第 v_ivi 朵云,同理,如果买第 v_ivi 朵就必须买第 u_iui 朵。
输出格式
一行,表示可以获得的最大价值。
输入输出样例
输入 #1复制
5 3 10 3 10 3 10 3 10 5 100 10 1 1 3 3 2 4 2
输出 #1复制
1
说明/提示
- 对于 30\%30% 的数据,满足 1 \le n \le 1001≤n≤100;
- 对于 50\%50% 的数据,满足 1 \le n,w \le 10^31≤n,w≤103,1 \le m \le 1001≤m≤100;
- 对于 100\%100% 的数据,满足 1 \le n \le 10^41≤n≤104,0 \le m \le 5 \times 10^30≤m≤5×103。
这题采用01背包和并查集,因为这个老板规定,必须搭配购买,买a必须买b,买b必须买a,因此将a的根节点指向b的根节点,然后可以把价格价值全部加在b的根节点上。所以购买时只需购买根节点的物品。
bug:每次联合两个节点的时候,没有用一个参数来代表根节点,因此每次都会需要寻找根节点。
void link(int a,int b)
{
if(find_root(a)!=find_root(b))
father[find_root(a)]=find_root(b);
}
还有一个bug:即数组开太小了,导致超出内存。
下午把代码改了改。
代码实现:
#include<bits/stdc++.h>
using namespace std;
int father[1000000];
int v[100001];//价钱
int w[100001];//价值
int f[100001];
int find_root(int a)
{
if(father[a]==a)
return a;
else{
father[a]=find_root(father[a]);
return father[a];
}
}
void link(int x,int y)
{
int a=find_root(x);
int b=find_root(y);
if(a!=b)
{
father[a]=b;
v[b]+=v[a];//连多云相连,所以需要把总价钱 总价值放在根节点上
w[b]+=w[a];
}
}
int main()
{
int n,m,W;
cin>>n>>m>>W;
for(int i=1;i<=n;i++)
{
father[i]=i;//父节点
cin>>v[i]>>w[i];
}
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
link(x,y);
}
for(int i=1;i<=n;i++)
{
if(father[i]==i)//找到一个根节点,然后进行背包
{
for(int j=W;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[W];
}
P1305 新二叉树
题目描述
输入一串二叉树,输出其前序遍历。
输入格式
第一行为二叉树的节点数 nn。(1 \leq n \leq 261≤n≤26)
后面 nn 行,每一个字母为节点,后两个字母分别为其左右儿子。
空节点用 *
表示
输出格式
二叉树的前序遍历。
输入输出样例
输入 #1复制
6 abc bdi cj* d** i** j**
输出 #1复制
abdicj
代码实现:
#include<bits/stdc++.h>
using namespace std;
struct node{
char l;//左儿子
char r;//右儿子
};
struct node tree[1000];
void print(char x)
{
cout<<x;//输出
if(tree[x].l!='*')//先向左遍历
print(tree[x].l);
if(tree[x].r!='*')//再向右遍历
print(tree[x].r);
}
int main()
{
int n;
cin>>n;
char r;
for(int i=1;i<=n;i++)
{
char a;
cin>>a;//输入一个字母表示根节点
if(i==1)
r=a;
cin>>tree[a].l>>tree[a].r;//输入根节点的左右儿子
}
print(r);
return 0;
}