9/6 分组背包+有依赖的背包+基环树
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9/6 分组背包+有依赖的背包+基环树相关的知识,希望对你有一定的参考价值。
分组背包
题意:物品被分为n个组,每组有s[i]个物品,组内各个物品可选可不选(0 or 1),要求在容量为V的情况下得到的价值最大是多少
int n,s[105],v[105][N],w[105][N],f[105][N],V;
//分组背包 朴素算法
void fun1()
for(int i=1;i<=n;i++)
for(int j=1;j<=V;j++)
for(int k=0;k<=s[i];k++)
if(j>=v[i][k])
f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
cout<<f[n][V]<<endl;
// 空间优化 先体积 再组内各个物品
void fun2()
for(int i=1;i<=n;i++)
for(int j=V;j>=v[i][k];j--)
for(int k=0;k<=s[i];k++)
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
cout<<f[V]<<endl;
有依赖的背包
树形dp+分组背包
简单来说就是把每个节点看成一个背包啦,它的容量就是以这个节点为根的子树大小,组数就是连接的儿子个数。
每组都有很多选择,选一个,两个,三个用户,把这些选择当做组中的元素就好了,容易看出每组中只能选一个元素:比如你选择了选一个用户,就不可能同时选择选两个用户。
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=3000+5;;
const int inf=1e18;
const int mod=19980829;
int n,m,cnt,head[N],val[N];
int f[N][N]; //以i为根节点,选取j个用户的最大价值是多少
struct node
int to,nxt,w;
e[N*N];
void add(int from,int to,int w)
e[++cnt].to=to;
e[cnt].w=w;
e[cnt].nxt=head[from];
head[from]=cnt;
int dfs(int u)
if(u>n-m)
f[u][1]=val[u];return 1;
int t,sum=0;
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;
t=dfs(v);
sum+=t;
for(int j=sum;j>0;j--) //用户总数
for(int k=1;k<=t;k++)
if(j>=k)
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-e[i].w);
return sum;
void solve()
cin>>n>>m;
memset(f,~0x3f,sizeof(f));
for(int i=1;i<=n-m;i++)
int k;cin>>k;
for(int j=1;j<=k;j++)
int v,w;cin>>v>>w;
add(i,v,w);
for(int i=n-m+1;i<=n;i++) cin>>val[i];
for(int i=1;i<=n;i++) f[i][0]=0;
dfs(1);
for(int i=m;i>=1;i--)
//cout<<f[1][i]<<endl;
if(f[1][i]>=0)
cout<<i<<endl;return;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
B. Madoka and Underground Competitions
啊啊 真的太蠢了,代码总是敲错,思考出规律,一行就解决了。。。笨死
思路:(i+j)%k==(r+c)%k
,注重一下下标间的关系
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=3000+5;;
const int inf=1e18;
const int mod=19980829;
int n,k,r,c;
bool vis[505][505];
void solve()
memset(vis,0,sizeof vis);
cin>>n>>k>>r>>c;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if((i+j)%k==(r+c)%k)
cout<<"X";
else
cout<<".";
cout<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
P1399 [NOI2013 ] 快餐店
思路真是太妙了,运送前缀和、后缀和来枚举区间,长见识了
目标:求出基环树的最大直径。
思路:
1.最大直径直径不经过基环,为基环树上某个点的最大子树直径
2.最大直径再基环上,若断开基环每条边,找出每两个点之间的最大直径(两个点的最大深度+基环上距离),复杂度为O(n*n)
3.最精妙的一个地方。通过预处理,求出前、后缀中最大长度,分割拼凑,选取最优区间,得到答案。
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=1e5+5;
const int inf=1e18;
const int mod=19980829;
int n,cnt,head[N];
int vis[N],fa[N];
int inc[N]; //标记环上的点
int cv[N]; //给环上的点编号
double cw[N]; //记录环上权值
int cn;
double ans1,ans2=inf;
double d[N],A[N],B[N],C[N],D[N],w[N];
struct node
int to,nxt;
double w;
e[N*2];
void add(int from,int to,double w)
e[++cnt].to=to;
e[cnt].w=w;
e[cnt].nxt=head[from];
head[from]=cnt;
int fd(int u) //找出环并编号
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;
if(v!=fa[u])
fa[v]=u,w[v]=e[i].w;
if(!vis[v])
if(fd(v)) return 1;
else
int p=u;
while(1)
inc[p]=1,cv[++cn]=p,cw[cn]=w[p],p=fa[p];
if(p==u) break;
return 1;
return 0;
void dfs(int u,int fa) //记录环上各个点的最大深度d[i]
// ans1 为不经过环的最大子树直径
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;
if(!inc[v]&&v!=fa)
dfs(v,u);
ans1=max(ans1,d[u]+d[v]+e[i].w);
d[u]=max(d[u],d[v]+e[i].w);
void solve()
cin>>n;
for(int i=1;i<=n;i++)
int u,v,w;cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
fd(1);
for(int i=1;i<=cn;i++)
dfs(cv[i],0);
double sum=0,mx=0;
for(int i=1;i<=cn;i++)
sum+=cw[i-1];
A[i]=max(A[i-1],sum+d[cv[i]]);
B[i]=max(B[i-1],mx+d[cv[i]]+sum);
mx=max(mx,d[cv[i]]-sum);
sum=mx=0;
double gg=cw[cn];cw[cn]=0;
for(int i=cn;i>=1;i--)
sum+=cw[i];
C[i]=max(C[i+1],sum+d[cv[i]]);
D[i]=max(D[i+1],mx+d[cv[i]]+sum);
mx=max(mx,d[cv[i]]-sum);
double res=0;
for(int i=1;i<cn;i++)
res=max(B[i],D[i+1],A[i]+gg+C[i+1]);
ans2=min(ans2,res);
printf("%.1lf\\n",max(ans2,ans1)/2);
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
以上是关于9/6 分组背包+有依赖的背包+基环树的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj1791][ioi2008]Island 岛屿(基环树树的直径)