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+分组背包

简单来说就是把每个节点看成一个背包啦,它的容量就是以这个节点为根的子树大小组数就是连接的儿子个数。

每组都有很多选择,选一个,两个,三个用户,把这些选择当做组中的元素就好了,容易看出每组中只能选一个元素:比如你选择了选一个用户,就不可能同时选择选两个用户。

P1273 有线电视网

#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 岛屿(基环树树的直径)

[IOI2008]Island

P4381 [IOI2008]Island

动态规划专题

[题解] LuoguP4381 [IOI2008]Island

IOI2008 island