DP专题测试联合题解

T1

在这里插入图片描述

T2

在这里插入图片描述

T3

在这里插入图片描述


以上是题面


考试的时候先看的第三题,感觉可以用模拟退火乱搞出来,但是后面发现这不是一个状压DP,所以打了一个比较玄学的dfs(给每条边赋了一个错误率),然后只骗到27分

第二题是想出了一个DP方程:f[i][j]是指i个点高度等于j的方案数,有方程
f [ i ] [ j ] = ∑ k = 1 , z = 1 k = i − 2 , z = j − 1 ( f [ k ] [ j − 1 ] × f [ i − 1 − k ] [ z ] ) + ∑ k = 1 , z = 1 k = i − 2 , z = j − 1 ( f [ k ] [ z ] × f [ i − 1 − k ] [ j − 1 ] ) f[i][j]=\sum_{k=1,z=1}^{k=i-2,z=j-1}(f[k][j-1]\times f[i-1-k][z])+\sum_{k=1,z=1}^{k=i-2,z=j-1}(f[k][z]\times f[i-1-k][j-1]) f[i][j]=k=1,z=1k=i2,z=j1(f[k][j1]×f[i1k][z])+k=1,z=1k=i2,z=j1(f[k][z]×f[i1k][j1]),过了小样例但没过大样例

第一题一眼就看出来是和noip货币系统极像,但是就是被那个2e9吓到了,又加上电脑爆炸和蹲厕所,就没有来得及打代码

按道理说正常情况下100分以上是可以得到的,但是尴尬的是这次用cena交代码,还没来得及保存到收集目录下电脑就被教师机控制了,所以最后只交了第三题的代码,还是自己太弱啊(下次考试一定先用dos关掉教师机嘿嘿嘿嘿嘿


接下来进入正题

T1

嘿嘿嘿,2e9吓死人,但其实本题的答案范围是不会超过 250 2 {250}^{2} 2502的(详情请见最大不可表数),这也就意味着可以用刷表的方式来解决本题

设f[i]指块数为3的包装可不可以被凑出来,则有方程 f [ i ] = f [ i ] ∣ f [ j ] , 1 &lt; = k &lt; = i f[i]=f[i]|f[j],1&lt;=k&lt;=i f[i]=f[i]f[j],1<=k<=i,n方刷一遍表在O(N)枚举一下就可以了

code

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
int n;
const int maxn=15;
const int maxw=256*256+100;
int a[maxn];
bool f[maxw];
template<typename T>void read(T &x){
    x=0;register char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("datain.txt","r",stdin);
    #endif // ONLINE_JUDGE
    clean(f,false),clean(a,0);
    read(n);
    loop(i,1,n)read(a[i]),f[a[i]]=true;
    loop(i,1,maxw)if(f[i]==true)
        loop(j,1,n)
            if(a[j]+i<=maxw)f[a[j]+i]=true;
    anti_loop(i,maxw,1)if(f[i]==false){
        if(i<=maxw-100)printf("%d\n",i);
        else printf("%d\n",0);
        exit(0);
    }
    printf("%d\n",0);
    return 0;
}

T2

这道题的状态比较有趣
其实上面的状态设计已经差不多接近比较正的解了,但由于“高度等于h”这个条件太“苛刻”了(呵呵),我们就可以改进一下,用f[i][j]表示i个节点高度小于等于j的方案数,于是有 f [ i ] [ j ] = ∑ k = 1 k = i − 2 f [ k ] [ j − 1 ] × f [ i − 1 − k ] [ j − 1 ] f[i][j]=\sum_{k=1}^{k=i-2} f[k][j-1]\times f[i-1-k][j-1] f[i][j]=k=1k=i2f[k][j1]×f[i1k][j1]确实简洁了不少),当然,边界就是 f [ 1 ] [ k ] = 1 ( 1 &lt; = k &lt; = n ) f[1][k]=1(1&lt;=k&lt;=n) f[1][k]=1(1<=k<=n),答案就是 f [ n ] [ k ] − f [ n − 1 ] [ k ] f[n][k]-f[n-1][k] f[n][k]f[n1][k]

注意: 这里要求要对9901取模,因此可能f[n][k]-f[n-1][k]<0,所以在输的时候就要对这个值加一个9901再取模以防为负数

code

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
const int maxn=200+10;
const int maxk=110;
const int mod=9901;
int n,k;
int f[maxn][maxk];
int main(){
    #ifndef ONLINE_JUDGE
    freopen("datain.txt","r",stdin);
    #endif // ONLINE_JUDGE
    clean(f,0);
    read(n),read(k);
    loop(i,1,k)f[1][i]=1;
    for(int i=3;i<=n;i+=2){
        loop(j,1,k+2){
            for(int z=1;z<=i-2;z+=2){
                f[i][j]+=f[z][j-1]*f[i-1-z][j-1];
                f[i][j]%=mod;
            }
        }
    }
    printf("%d\n",(f[n][k]-f[n][k-1]+mod)%mod);
    return 0;
}

T3

这题当时根本就没往动归方向去想,以至于看到题解的时候有点懵逼
假定有两个人a和b,f[i][j]表示a到达i,b到达j,则有
f [ i ] [ j ] = m a x ( f [ i ] [ k ] + 1 , f [ i ] [ j ] ) f[i][j]=max(f[i][k]+1,f[i][j]) f[i][j]=max(f[i][k]+1,f[i][j])由于f[i][j]=f[j][i],则没有必要更新f[j][i],到时候直接赋值就好
注意这里我们强制j>i,j>k则i,j不可重复且j,k不可重复,为了保证i,k不重复,我们在代码中加一个判定:当且仅当f[i][k]!=0时才用其更新,这样就保证了
1.f[i][i]不合法
2.任意一个状态f[i][j],i!=j中不会包含有f[i][i],1<=i<=n的情况
这样就保证了最后答案中路径不会有交集

code

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define ll long long
const int maxn=100+10,mod=18960421;
bool G[maxn][maxn];
int f[110][110];
char name[20];
map<int,int>m;
int S;
int n,v;
inline int hash(char *r){
	int len=strlen(r);
	int res=0;
	for(int i=0;i<len;++i)res=(res*100+r[i]-'A'+10)%mod;
	return res;
}
template<typename T>void read(T &x){
    x=0;char r=getchar();T neg=1;
    while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
    while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
    x*=neg;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("datain.txt","r",stdin);
    #endif // ONLINE_JUDGE
    read(n),read(v);
    for(register int i=1;i<=n;++i){
		scanf("%s",name);if(i==1)S=hash(name);
		m[hash(name)]=i;
	}
    for(int i=1;i<=v;++i){
		int xi,yi;
		scanf("%s",name);xi=hash(name);scanf("%s",name);yi=hash(name);
		G[m[xi]][m[yi]]=G[m[yi]][m[xi]]=true;
	}
	f[1][1]=1;
	loop(i,1,n-1){
        loop(j,i+1,n){
            loop(k,1,j-1){
                if (G[j][k]&&f[i][k])
                    f[i][j]=max(f[i][j],f[i][k]+1);
            f[j][i]=f[i][j];
            }
        }
	}
	int _maxx=1;
	loop(i,1,n){
        if(G[i][n])_maxx=max(_maxx,f[i][n]);
	}
	printf("%d\n",_maxx);
    return 0;
}


不念过去,不惧将来,如此安好


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值