牛客OI周赛15-提高组(A 树形dp求子树直径 B 贪心,C(bitset 优化01背包))

题目链接

A-环球旅行

题意:

做法:参考来自:

vector超时,用链式前向星才能A

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=1e6+10;
struct node
{
    int u,w,ne;
}e[N*2];
int head[N],cnt;
void add(int u,int v,int w)
{
    e[++cnt]={v,w,head[u]};
    head[u]=cnt;
    e[++cnt]={u,w,head[v]};
    head[v]=cnt;
}

vector<pii >edge;
int n;
int mx[N][2],f[N][2];
int sum=0,flag,l,r;
void dfs1(int u,int fa,int dep)
{
    if(dep>sum){
        sum=dep;
        if(!flag) l=u;
        else r=u;
    }
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        dfs1(v,u,dep+w);
    }
}
void dfs2(int u,int fa)
{
    if(u==r){
        flag=1;return;
    }
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        edge.push_back(make_pair(u,v));
        dfs2(v,u);
        if(flag) return ;
        edge.pop_back();
    }
}
void init()
{
    dfs1(1,-1,0);
    sum=0,flag=1;
    dfs1(l,-1,0);//获取直径的两个点
    flag=0;
    dfs2(l,-1);//获取直径的信息
}
void dfs3(int u,int fa,int flag)
{
    int mx1=0,mx2=0;
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].u;
        int w=e[i].w;
        if(v==fa) continue;
        dfs3(v,u,flag);
        int t=mx[v][flag]+w;

        if(t>=mx1) mx2=mx1,mx1=t;
        else if(t>mx2) mx2=t;

        mx[u][flag]=max(mx[u][flag],mx[v][flag]+w);
        f[u][flag]=max(f[u][flag],f[v][flag]);
    }
    //if(!flag) printf("u:%d mx1:%d mx2:%d\n",u,mx1,mx2);
    f[u][flag]=max(f[u][flag],mx1+mx2);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);

    }
    init();
    //printf("l:%d r:%d\n",l,r);

    dfs3(l,-1,0);
    dfs3(r,-1,1);


//    for(int i=1;i<=n;++i){
//        printf("i:%d r:%d mx:%d\n",i,f[i][0],mx[i][0]);
//    }


    int ans=1e9;
    for(auto now:edge)
    {
        ans=min(ans,max(f[now.first][1],f[now.second][0]));
    }
    cout<<ans<<endl;
}

B-恢复数列

题意:

从最高位一个 往后面开始拆,每次拆都会损失一个,新增六个,于是真正的新增是5个,题目保证有解。

那么从最高位n开始拆就可以了,运用一下队列即可。由于还要输出数列ai,那么就需要记录历史状态不可Pop用一个vector保存,用一个左指针去找可以拆的数即可。

#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int>>ans;
int n,x;
int main()
{
    scanf("%d%d",&n,&x);
    int len=x;
    ans.push_back(make_pair(x,n-1));
    int i=0;
    while(len<n-1){
        while(ans[i].first==0) ++i;
        len+=x-1;
        ans[i].first--;
        ans.push_back(make_pair(x,ans[i].second-1));
    }
 
    printf("%d ",n);
    for(auto now:ans){
        while(now.first--) printf("%d ",now.second);
    }
}

C-回到过去

题意:

求出哪些胶囊是必带的。

样例解析:

可以简单的想到01背包思想:

于是超时代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
//bitset<N>pre[500],last[500];
int a[N],n,m;
bool pre[500][N],last[500][N];
map<int,int>mp;
int main()
{
    cin>>n>>m;
    rep(i,1,n){scanf("%d",&a[i]);mp[a[i]]++;}
    sort(a+1,a+1+n);n=unique(a+1,a+1+n)-a-1;
 
 
     
    pre[0][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=0;j<=m;++j){
            pre[i][j]=pre[i-1][j];
            int t=mp[a[i]];
            for(int k=1;k<=t;++k)
            if(j>=k*a[i]) pre[i][j]|=pre[i-1][j-k*a[i]];
        }
    }
 
    last[n+1][0]=1;
    for(int i=n;i>=1;--i){
        for(int j=0;j<=m;++j){
            last[i][j]=last[i+1][j];
            int t=mp[a[i]];
            for(int k=1;k<=t;++k)
            if(j>=k*a[i])last[i][j]|=last[i+1][j-k*a[i]];
        }
    }
 
 
    vector<int>ans;
    for(int i=1;i<=n;++i){
        int flag=1;
        for(int j=0;j<=m&&flag;++j){
            if(pre[i-1][j]&&last[i+1][m-j]){
                flag=0;
            }
        }
        if(flag){
            ans.push_back(a[i]);
        }
    }
 
    sort(ans.begin(),ans.end());
    printf("%d\n",ans.size());
    for(int v:ans)printf("%d ",v);
}

接着了解一下bitset优化01背包

对应题型:对于这类  种类不同,又有数量的 >=1  由于    种类间的数量之和   是 1e5  状态也是  1e5   普通的 01  背包肯定超时。

bitset优化呢,采取了  二进制的特性,同种类型的数量  可以通过倍增的方法进行迭代  覆盖状态,有点像快速幂。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
#include<bitset>
using namespace std;
typedef long long ll;
const int N = 100010;
bitset<N>f[451],g[451];

 
int a[N],cnt[N],ans[N],anss;
int main() {
   
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
 
    sort(a + 1,a + n + 1);
 
 
    int tot = 0;
    for(int i = 1;i <= n;++i) {
        if(a[i] != a[i - 1])  a[++tot] = a[i];
        cnt[tot]++;
    }
 
    n = tot;
 
    f[0][0] = 1;
    g[n + 1][0] = 1;
     
    for(int i = 1;i <= n;++i) {
        int k = a[i],tmp = cnt[i];//f[i]|=f[i]<<a[i]代表f[i]取一个a[i]时的状态转移 
         
        f[i] = f[i - 1];
         
        for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) f[i] |= f[i] << k;
        // 运用倍增(二进制)的思想,节约时间 并且能够覆盖所有的状态
        //比如现在有5个1  
        //j=1 1一个1  可以 得到 bitset状态:000011(从后往前数从低位到高位,低位从0开始)
        //j=2 那么倍增一下,两个1 :之前的状态00011移两位得  001100 
        //或上之前得000011  得    001111   是不是得到0,1,2,3,都是1的情况
        //需要注意的是现在我们应该是消耗了三个1了 j目前还是2。那么tmp就不是一成不变的,所以tmp-=j
        //接着j继续乘2  j =4  由于5个1消耗了3  剩余两个,小于j   跳出for循环
         //因为有剩余的部分,就继续组合一下
        f[i] |= f[i] << (a[i] * tmp);
    }
 
    for(int i = n;i >= 1;--i) {
        int k = a[i],tmp = cnt[i];
         
        g[i] = g[i + 1];
         
        for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) g[i] |= g[i] << k;
     
        g[i] |= g[i] << (a[i] * tmp);
    }
 
    for(int i = 1;i <= n;++i) {
        int flag = 0;
        for(int j = 0;j <= m;++j) {
            if(f[i - 1][j] & g[i + 1][m - j]) {
                flag = 1;break;
            }
        }
        if(!flag) ans[++anss] = a[i];
    }
 
//  for(int i = 1;i <= n;++i) printf("%d %d\n",a[i],cnt[i]);
 
    printf("%d\n",anss);
    for(int i = 1;i <= anss;++i) printf("%d ",ans[i]);
    return 0;
}
发布了519 篇原创文章 · 获赞 69 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/105324165