三道题

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

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\n",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\n",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\n1 1");
    }
    else if(n==3){
        printf("2\n1 1\n2 2 3");
    }
    else {
        printf("%d\n",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("\n");
            }
        }
        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("\n");
            }
        }
        /*else if(n==4){
            printf("2\n1 1\n2 2 3");
        }
        else if(n==6){
            printf("4\n1 1\n2 2 3\n3 2 4 5\n4 4 5 6");
        }*/
    }
}

猜你喜欢

转载自www.cnblogs.com/anyixing-fly/p/12755411.html