zoukankan      html  css  js  c++  java
  • 2017 清北济南考前刷题Day 4 morning

    考场思路:

    倒着算就是

    可以对一个数-1

    可以合并两个数

    可以证明只有0和0才能执行合并操作

    然后模拟

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 1000001
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int a[N];
    
    int main()
    {
        //freopen("multiset.in","r",stdin);
    //    freopen("multiset.out","w",stdout);
        int n;
        read(n); 
        int sum0=0,cnt=0,x;
        for(int i=1;i<=n;i++) 
        {
            read(x);
            if(!x) sum0++;
            else a[++cnt]=x;
        }
        sort(a+1,a+cnt+1);
        long long ans=0,gather=0;
        for(int i=1;i<=cnt;i++)
        {
            if(!a[i]) break;
            a[i]-=gather;
            x=a[i]; gather+=x; ans+=x;
            while(x) 
            {
                x--;
                if(sum0>1) sum0=sum0+1>>1;
                else break;
            }
            sum0++;
            while(i<cnt && a[i+1]-gather==0)
            {
                sum0++;
                a[++i]-=gather;
            }
        }
        while(sum0>1) ans++,sum0=sum0+1>>1;
        cout<<ans;
    }
    View Code

    考场上没注意有向图。。。。

    一条道路如果能在上一组,那么肯定把它放在上一组最优

    所以可以没加一条边,就判断当前1和n是否联通

    判断方式: u-->v若现在u没有与1联通,就不管他

    若u和v都与1联通了,那也不管他

    若 u与1联通,而v 没有联通,那就再从v开始bfs

    这样 每条边只会被访问一次

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    #define N 200001
    #define M 500001
    
    int n;
    
    int front[N],nxt[M],to[M],tot;
    
    bool vis[N]; int use[N],cnt;
    
    queue<int>q;
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    void add(int u,int v)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    }
    
    bool bfs(int s)
    {
        if(s==n) return true;
        while(!q.empty()) q.pop();
        q.push(s);
        int now;
        while(!q.empty())
        {
            now=q.front(); q.pop();
            for(int i=front[now];i;i=nxt[i])
                if(!vis[to[i]])
                {
                    use[++cnt]=to[i];
                    if(to[i]==n) return true;
                    vis[to[i]]=true;
                    q.push(to[i]);
                }
        }
        return false;
    }
    
    int main()
    {
        freopen("road.in","r",stdin);
        freopen("road.out","w",stdout);
        int m;
        read(n); read(m);
        int u,v; int ans=1;
        vis[1]=true;
        for(int i=1;i<=m;++i)
        {
            read(u); read(v);
            if(vis[u] && !vis[v]) 
            {
                add(u,v);
                vis[v]=true;
                use[++cnt]=v;
                if(bfs(v))
                {
                //    printf("%d
    ",i);
                    for(int i=1;i<=cnt;++i) vis[use[i]]=false,front[use[i]]=0;
                    front[1]=0;
                    cnt=tot=0; ans++;
                    add(u,v);
                    if(u!=1) use[++cnt]=u;
                    if(u==1) vis[v]=true,use[++cnt]=v;
                }
            }
            else if(!(vis[u] && vis[v])) add(u,v),use[++cnt]=u;    
        }
        cout<<ans;
    }
    View Code

    std思路:

    结合了倍增的二分

    如果用朴素的二分,会被m条边分m组卡成mm

    先考虑1条边 能否使其联通,不能再考虑2条边,然后4条,8条……

    若在2^p时 不能联通了,那么在2^p-1 ~ 2^p 范围内二分

    这样时间复杂度是mlogm的

     如果小兵的血量是1 2 3 4 5 ……

    那么显然我们可以补到所有的兵

    如果有相同血量的兵,那么只能补到其中的1个兵

    所以我们要尽可能的把给出的兵的血量变成1 2 3 4 5 ……

    一种可行的方案是 重复血量的兵 强制消耗代价使他 变成 血量更小 但血量不重复的兵

    可以用栈记录之前没有的血量,每遇到一个重复的就用栈中的一个血量

    例:1 2 4 4

    扫到4的时候,之前没有血量3,3入栈

    后面还是1个4,就让3出栈,即消耗代价 使4变成3

    令c[i]=j 记录消耗代价后血量为i的兵实际上是原血量为j的兵

    然后DP

    dp[i][j] 表示到血量为i的兵,省下了j刀的最大补兵数

    省下了j刀:就是先把兵的血量看成1,2,3,然后考虑每个兵砍或不砍。如果不砍,就可以省下一刀给以后用 

     所以如果不砍,状态转移为 dp[i][j]=dp[i-1][j-1]

    如果砍的话,砍血量为i的兵要加上之前强制消耗的代价,所以dp[i][j]=dp[i-1][j+c[i]-i]+1

    老鹿的攻击怎么体现的呢?

    因为每次尽可能的让兵的血量变为1,2,3……

    自己砍掉一个血量为1的兵,后面再老鹿的攻击下又产生了一个血量为1的兵

    但实际DP时

    从1开始枚举血量

    血量为2时,实际血量为1,相当于 提高了 兵死亡时的血量

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    
    using namespace std;
    
    #define N 1001
    
    int f[N][N];
    
    int a[N],c[N];
    
    int cnt[N];
    
    int st[N],top;
    
    int main()
    {
        freopen("cs.in","r",stdin);
        freopen("cs.out","w",stdout);
        int T,n,mx;
        scanf("%d",&T);
        while(T--)
        {
            memset(f,0,sizeof(f));
            memset(cnt,0,sizeof(cnt));
            memset(c,0,sizeof(c));
            top=mx=0;
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]),mx=max(mx,a[i]),cnt[a[i]]++;
            sort(a+1,a+n+1);
            for(int i=1;i<=mx;i++)
            if(!cnt[i]) st[++top]=i;
            else
            {
                while(cnt[i]>1 && top ) c[st[top--]]=i,--cnt[i];
                c[i]=i;
            }
            int ans=0;
            for(int i=1;i<=mx;++i)
                for(int j=0;j<i;++j)
                {
                    if(j) f[i][j]=f[i-1][j-1];
                    if(c[i] && j+c[i]-i<i) f[i][j]=max(f[i][j],f[i-1][j+c[i]-i]+1);
                    ans=max(ans,f[i][j]);
                } 
            cout<<ans<<'
    '; 
        } 
    }
    View Code
  • 相关阅读:
    08-Linux命令【rm】
    07-Linux命令【mv】
    06-Linux命令【cp】
    05-Linux命令【rmdir】
    04-Linux命令【mkdir】
    03-Linux命令【ls】
    02-Linux命令【cd】
    01-Linux命令【pwd】
    智慧城市3D园区
    自我觉醒
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7761063.html
Copyright © 2011-2022 走看看