zoukankan      html  css  js  c++  java
  • 三道题

    还是按难度递增的顺序放吧。

    T1 https://www.luogu.com.cn/problem/P3360

    这个既然它给的顺序是一个dfs序,那直接边dfs边读入就完了,因为它既有体积也有价值,所以枚举每个走廊分配多少时间就可以了,就是一个树上的背包问题。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=600;
    ll dp[N][N],v;
    int n,w,cnt;
    void dfs(int u){
        int t,x;
        scanf("%d%d",&t,&x);
        t<<=1;
        if(x){
            for(int i=1;i<=x;i++){
                scanf("%lld%d",&v,&w);
                for(int j=n;j>=t+w;j--){
                    dp[u][j]=max(dp[u][j],dp[u][j-w]+v);
                }
            }
            return ;
        }
        int l=++cnt;dfs(l);
        int r=++cnt;dfs(r);
        for(int i=t;i<=n;i++)
            for(int j=0;i>=j+t;j++)
                dp[u][i]=max(dp[u][i],dp[l][j]+dp[r][i-j-t]);
    }
    int main(){
        scanf("%d",&n);
        n--;
        cnt=1;
        dfs(1);
        printf("%lld
    ",dp[1][n]);
    }
    

    T2 https://www.luogu.com.cn/problem/P4303

    这题其实算是挺裸了,如果不看数据范围,就跑一个LCS就完了。但是它数据范围很大,所以应该考虑LCS的NlogN写法,直接套那个板子显然不行,因为有重复的,并且重复次数是相同的,那么也就是说,另一个序列有且只有5个位置可以让LCS的长度更新,所以我把这5个位置记录下来然后按照那个NlogN的写法写,然后依次枚举试图更新答案就ok。但有一个问题,就是如果这五个位置依次是1,2,3,4,5,那么这可能一次更新就更新了5,这不就over了吗?因此为了防止这种情况发生,需要倒着枚举。

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N=1e5+10;
    int w,n;
    int tr[N];
    int lowbit(int i){
        return i&-i;
    }
    void updata(int x,int val){
        while(x<=n){
            tr[x]=max(tr[x],val);
            x+=lowbit(x);
        }
    }
    int find(int x){
        int res=0;
        while(x){
            res=max(res,tr[x]);
            x-=lowbit(x);
        }
        return res;
    }
    int main(){
        vector<int > pos[N];
        scanf("%d",&n);
        n*=5;
        for(int i=1;i<=n;i++){
            scanf("%d",&w);
            pos[w].push_back(i);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&w);
            for(int j=pos[w].size()-1;j>=0;j--)
                updata(pos[w][j],find(pos[w][j]-1)+1);
        }
        printf("%d
    ",find(n));
    }
    

    T3


    这个题第一眼看还是毫无头绪的,当然除了打表。所以就只能打表了呗。
    打表首先发现的是第一问的答案为n-2和一些特判,于是printf n-2,20分有了。
    当时没怎么想证明,现在要来证明一下。
    最大方案数为n的情况,显然不存在,必定会包含。那n-1呢?1的那个方案肯定和n-1的并集应该是n,而2的方案理应是不能含1的,那~,请问这个2的方案怎么生成,是不是,所以只能是n-2。
    其实这个证明也不是很严谨吧,但也就这么理解一下吧。
    所以就来考虑生成这n-2个方案,打表肯定是能打出来,但打着打着就………………没有然后了,那有没有一种方法可以很简单的由一种方案生成另一种方案呢?比如下边这个:
    (x_1)
    (x_2) (x_3)
    (x_3) (x_4) (x_5)
    考虑后边的方案可以怎么得出,显然我可以在每个方案后边加一个数,然后每一行都向后推一行,第一行就空出来了,放另外一个数就行。最后一行摆上1到n-2,生成完毕,所以这个题就变成了一个神奇的模拟题,然后就打A了。。。。。
    就还有一个问题,这种生成方式只能由n-2推到n,所以要考虑给定的数是奇数还是偶数。

    #include<cstdio>
    using namespace std;
    const int N=1e3+10;
    int num[N][N];
    int main(){
    //    freopen("course.in","r",stdin);
    //    freopen("course.out","w",stdout);
        int n;
        scanf("%d",&n);
        if(n==1||n==2){
            printf("1
    1 1");
        }
        else if(n==3){
            printf("2
    1 1
    2 2 3");
        }
        else {
            printf("%d
    ",n-2);
            num[1][1]=1;num[2][1]=2;num[2][2]=3;
            if(n&1){
                for(int i=5;i<=n;i+=2){
                    for(int j=1;j<=i-4;j++)
                        num[j][j+1]=i;
                    for(int j=i-4;j>=1;j--)
                        for(int k=1;k<=j+1;k++)
                            num[j+1][k]=num[j][k];
                    num[1][1]=i-1;
                    for(int j=1;j<=i-2;j++)
                        num[i-2][j]=j;
                }
                for(int i=1;i<=n-2;i++){
                    printf("%d ",i);
                    for(int j=1;j<=i;j++)printf("%d ",num[i][j]);
                    printf("
    ");
                }
            }
            else{
                for(int i=6;i<=n;i+=2){
                    for(int j=1;j<=i-4;j++)
                        num[j][j+1]=i;
                    for(int j=i-4;j>=1;j--)
                        for(int k=1;k<=j+1;k++)
                            num[j+1][k]=num[j][k];
                    num[1][1]=i-1;
                    for(int j=1;j<=i-2;j++)
                        num[i-2][j]=j;
                }
                for(int i=1;i<=n-2;i++){
                    printf("%d ",i);
                    for(int j=1;j<=i;j++)printf("%d ",num[i][j]);
                    printf("
    ");
                }
            }
            /*else if(n==4){
                printf("2
    1 1
    2 2 3");
            }
            else if(n==6){
                printf("4
    1 1
    2 2 3
    3 2 4 5
    4 4 5 6");
            }*/
        }
    }
    
  • 相关阅读:
    洛谷 P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…(树规)
    STREAMING #5 题解 3.高位网络
    冲刺NOIP2015提高组复赛模拟试题(五) 3.破坏基地
    冲刺NOIP2015提高组复赛模拟试题(五)2.道路修建
    冲刺NOIP2015提高组复赛模拟试题(五)1.数学作业
    洛谷P1186 玛丽卡 spfa+删边
    清北学堂 day6 花
    清北学堂 day6 兔子
    C++ STL 全排列函数
    flash分区的意义
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12755411.html
Copyright © 2011-2022 走看看