今天结束了背包类的题
明天就开始状态机和状态压缩类的拉~
今天和同学聊起来好像进不了省选
非常遗憾的同时 也感觉自己技不如人 甘拜下风啊!
好像今天的训练时长又要短了 感觉自己就是又菜又想拿奖
思想问题很大啊!
AcWing 1013. 机器分配
我超 今天怎么会是
简单题 没看出分组背包 也只会暴力输出路径 没加if多调十多分钟呃呃
感觉已经废了
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
const int M = 1e4+10;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int f[N][N],n,m,path[N],cnt;
pair<int,int>p[N][N];
void dfs(int i,int j){
if(!i&&!j)return;
for(int k=0;k<=m;k++){
if(f[i-1][j-p[i][k].first]+p[i][k].second==f[i][j]){
path[++cnt]=k;
dfs(i-1,j-k);
return;
}
}
}
signed main(){
fast
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
p[i][j].first=j;
cin>>p[i][j].second;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i][j]=f[i-1][j];
for(int k=1;k<=m;k++){
if(j>=p[i][k].first)f[i][j]=max(f[i][j],f[i-1][j-p[i][k].first]+p[i][k].second);
}
}
}
cout<<f[n][m]<<endl;
dfs(n,m);
for(int i=1;i<=cnt;i++){
cout<<i<<' '<<path[n-i+1]<<endl;
}
return ~~(0^_^0);
}
426. 开心的金明
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4+10;
const int M = 1e4+10;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int f[N];
signed main(){
fast
int V,n;cin>>V>>n;
for(int k=1;k<=n;k++) {
int v,x;cin>>v>>x;
for(int j=V;j>=v;j--){
f[j]=max(f[j],f[j-v]+v*x);
}
}
cout<<f[V]<<endl;
return ~~(0^_^0);
}
10. 有依赖的背包问题
难 但是没有多重3难 毕竟我从来没学过树形dp
不过也不是不能想的那种 注释代码里写了
提一嘴思路
为什么是树形? 这个图给的就是树形 你说呢
以前我们都是咋枚举决策的 基本上是都把k层 暴力for一遍
但是这个就不一样了 因为你状态很多 基本上是2^n
所以我们会把状态表示关于V
这样子的O(nVV)
#include <bits/stdc++.h>
using namespace std;
const int N = 100+10;
const int M = 1e4+10;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,m,h[N],e[N<<1],ne[N<<1],idx,w[N<<1],v[N<<1],root,f[N][N];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u){
for(int i=h[u];~i;i=ne[i]){
int s=e[i];
dfs(s);//先递归到最底层,自下往上开始dp
for(int j=m-v[u];j>=0;j--){//我们不妨设我们背包大家被减去了vu,不然不好写,那自然后面要更新
for(int k=0;k<=j;k++){
f[u][j]=max(f[u][j],f[u][j-k]+f[s][k]);//这里我才搞清楚,我们dp是让几个子节点,一定混合都有枚举出最优解,
} //最开始想的是 这个不是只和一个子节点 枚举了一遍吗
} //其实他是每个子节点都枚举了一遍 我们只是每次跳出去更新了一次
}
for(int i=m;i>=v[u];i--)f[u][i]=f[u][i-v[u]]+w[u];//这里开始就是更新,要是v大于vu的自然要加上wu
for(int i=0;i<v[u];i++)f[u][i]=0;//小于的就根本进不去,也不准用其值,我们必须置为0
}
signed main(){
fast
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int c;cin>>v[i]>>w[i]>>c;
if(c==-1)root=i;//和编号连边,根节点我们也只要编号
else add(c,i);
}
dfs(root);//从根节点递归
cout<<f[root][m]<<Endl;//f表示为i节点下并且选择i节点的j体积下的max
return ~~(0^_^0);
}
11. 背包问题求方案数
和我想的一摸一样呢 再维护一个cnt数组就可以了!
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
const int M = 1e4+10;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int f[N],g[N],n,m;
signed main(){
fast
cin>>n>>m;
g[0]=1;
int maxv=0;
for(int i=1;i<=n;i++){
int v,w;cin>>v>>w;
for(int j=m;j>=v;j--){
int mx=max(f[j],f[j-v]+w);
int cnt=0;
if(mx==f[j])cnt+=g[j];
if(mx==f[j-v]+w)cnt+=g[j-v];
g[j]=cnt%mod;
maxv=max(maxv,mx);
f[j]=mx;
}
}
int cnt=0;
for(int i=0;i<=m;i++)if(maxv==f[i])cnt=(cnt+g[i])%mod;
cout<<cnt<<endl;
return ~~(0^_^0);
}
12. 背包问题求具体方案
这道题可以用string 数组来记录 路径 正着做
也可以反着做
而反着做的话我们的f_i_j i层就是从n到1
这样的状态表示的意思大概就是 i~n个物品 容量为j 时的max
我们的max自然是f_1_m
然后就可以从1_m开始转换
要是1_m可以被选择那么就是f[i][j]==f[i+1][j-v[i]]+w[i]
反之就是不能被选f_i_j==f_i+1_j
每次被选过后我们f_i_j就会转换为下一层 j也会变换
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
const int M = 1e4+10;
const int mod = 1e9+7;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,m,f[N][N],v[N],w[N];
signed main(){
fast
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=n;i>=1;i--){
for(int j=1;j<=m;j++){
f[i][j]=f[i+1][j];
if(j>=v[i])f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);
}
}
int j=m;
for(int i=1;i<=n;i++){
if(j>=v[i]&&f[i][j]==f[i+1][j-v[i]]+w[i]){
cout<<i<<' ';
j-=v[i];
}
}
return ~~(0^_^0);
}
487. 金明的预算方案
有依赖的背包问题 又是数组开小了卡很久
只注意到了都是10的整数倍
然后t了一发 加O2 能过 大概是O(nVV/100) 就是8的级别
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4+2010;
const int M = 1e4+10;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,m,h[N],e[N],ne[N],idx,w[N],v[N],f[65][N];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u){
for(int i=h[u];~i;i=ne[i]){
int s=e[i];
dfs(s);
for(int j=m-v[u];j>=0;j-=10){
for(int k=0;k<=j;k+=10){
f[u][j]=max(f[u][j],f[u][j-k]+f[s][k]);
}
}
}
for(int i=m;i>=v[u];i-=10)f[u][i]=f[u][i-v[u]]+w[u];
for(int i=0;i<v[u];i+=10)f[u][i]=0;
}
signed main(){
fast
cin>>m>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int c;cin>>v[i]>>w[i]>>c;
w[i]*=v[i];
add(c,i);
}
dfs(0);
cout<<f[0][m]<<Endl;
return ~~(0^_^0);
}
但是我们好像可以发现这里还有一句话
每个主件可以有0个、1个或2个附件。
那我们可以变成一个分组背包问题 每次反正要跟上主件
734. 能量石
一遍就写过了 感觉还不错
首先拿到这道题
我们都会直接想 直接01背包dp就是了
可是为啥不行呢
因为我们dp时 要求的 顺序是无序的
也就是如果直接把这个拿进去dp
f[j]=max(f[j],f[j-s]+e-(j-s)*l);
这个方程会因为顺序的固定让s只有一种情况
肯定是错误的
而我们贪心的作用就是 找出肯定正确的那个s
然后再来dp
为啥还要dp?
其实我们贪心出来的 只是一个最优解次序
吃不吃 这个其实我们是不得而知的
证一下这个贪心为啥正确
我们假设有俩数 i,i+1
要是先算i再算i+1 要是先算i+1再算i
Ei+Ei+1-Si*Li+1 Ei+1 + Ei + Si+1 * Li
删去相同项 我们 可以发现 就是 Si*Li+1 和 Si+1 * Li 相比谁在前 我们的总价值会不减少
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
const int M = 1e4+10;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define Endl '\n'
#define _ 0
#define inf 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int n,t,m,f[N];
struct Stone{
int s,e,l;
bool operator < (const Stone&W)const{
return W.l * s < W.s * l;
}
}stone[N];
signed main(){
fast
cin>>t;
for(int C=1;C<=t;C++){
m=0;
cin>>n;
memset(f,0,sizeof f);
for(int i=1;i<=n;i++){
int s,e,l;cin>>s>>e>>l;
m+=s;
stone[i]={s,e,l};
}
sort(stone+1,stone+n+1);
int res=0;
for(int i=1;i<=n;i++){
int s=stone[i].s,e=stone[i].e,l=stone[i].l;
for(int j=m;j>=s;j--){
f[j]=max(f[j],f[j-s]+e-(j-s)*l);
res=max(res,f[j]);
}
}
printf("Case #%d: %d\n",C,res);
}
return ~~(0^_^0);
}