哈尔滨理工大学软件学院ACM程序设计全国邀请赛【不断更新】

C:给定N和M,要求用N个数,这些数的和要能够覆盖1~M中的所有数,求这些数的方案总数

看到N和M最大200,猜想是打表,但是没想明白怎么搞:看到标程,一眼题


dp【i】【j】【k】:用i个数,这些数的和要能够覆盖1~M中的所有数,其中最大数是k

要求最后的答案ans=dp【n】【m】【1】+dp【n】【m】【2】+dp【n】【m】【3】+……+dp【n】【m】【m】

转移怎么想呢:

枚举可以取到的最大值,至少为k,最大为j+1,还要满足和不超过极限值200的条件

所以,全部可以提前打表预处理好的

#include<bits/stdc++.h>
using namespace std;

#define LL long long
#define mod 2000000007

const int maxn=220;
LL dp[maxn][maxn][maxn],ans;
int T,n,m;

int main(){
    memset(dp,0,sizeof(dp));
    dp[1][1][1]=1;
    for(int i=1;i<=200;i++)
        for(int j=1;j<=200;j++)
            for(int k=1;k<=200;k++){
                if (dp[i][j][k]==0) continue;
                for(int Max=k;Max+j<=200&&Max<j+2;Max++)
                    dp[i+1][j+Max][Max]=(dp[i+1][j+Max][Max]+dp[i][j][k])%mod;
            }
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        ans=0;
        for(int Max=1;Max<=m;Max++)
            ans=(ans+dp[n][m][Max])%mod;
        printf("%lld\n",ans);
    }
    return 0;
}



E:求有多少个连续区间里,全部是6

直接从前到后扫描一遍就好了,注意int和long long

#include<bits/stdc++.h>
using namespace std;

#define LL long long

const int maxn=2e5+50;

char s[maxn];
LL ans;
int T,n,num;

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%s",&n,s);
        ans=0;num=0;
        for(int i=0;i<n;i++){
            if (s[i]=='6') num++;
            else num=0;
            ans+=num;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


L题先给题目一个好评,再给自己一个中评,分析:
看到这种四个这么大的数字,而且还有非法的情况-1要判断,肯定是个构造题(想到了可怕的青岛B题魔方)这种题分析思路更重要吧,写出心路历程


首先,a3和a4比较重要,因为判断构造的形式


如果a3=a4,68686或者86868都可以
如果a3=a4+1,那么68要比86多一个,686868这样的
如果a4=a3+1,那么868686这样的


我们不能一直这样分类下去:在尝试无法构造其他的可能情况下,试图证明:
假设a3=a4+2,那么必须要出现6868这种的两个子段,那么用什么数字连接呢?
686866868和686886868,注意:这是可能的两种连接方式,但是,都会产生出来一个86!所以,不能出现a3>=a4+2的情况,同理对称的情况就不会出现了

总情况分类就是上面的三种:接下来需要数数,判断6和8的个数是不是可以满足至少的要求!
多的8是尽可能放在后面的,多的6是尽可能放在前面的(废话:因为要求数最小)


有了这些,分类打表就不难处理了

#include<bits/stdc++.h>
using namespace std;

int a1,a2,a3,a4;

int main(){
	while(scanf("%d%d%d%d",&a1,&a2,&a3,&a4)!=EOF){
		if (abs(a3-a4)>1) puts("-1");
		else if (a3==a4){
			if (a1>a3&&a2>=a3){
				for(int i=1;i<=a1-a3;i++) printf("6");
				for(int i=1;i<a3;i++) printf("86");
				for(int i=1;i<=a2-a3;i++) printf("8");
				puts("86");
			}
			else if (a1>=a3&&a2>a3){
				printf("86");
				for(int i=1;i<=a1-a3;i++) printf("6");
				for(int i=1;i<a3;i++) printf("86");
				for(int i=1;i<=a2-a3;i++) printf("8");
				puts("");
			}
			else puts("-1");
		}
		else if (a3==a4+1){
			if (a1>=a3&&a2>=a3){
				for(int i=1;i<=a1-a3;i++) printf("6");
				for(int i=1;i<=a3;i++) printf("68");
				for(int i=1;i<=a2-a3;i++) printf("8");
				puts("");
			}
			else puts("-1");
		}
		else if (a4==a3+1){
			if (a1>=a4&&a2>=a4){
				printf("86");
				for(int i=1;i<=a1-a4;i++) printf("6");
				for(int i=1;i<=a4-2;i++) printf("86");
				for(int i=1;i<=a2-a4;i++) printf("8");
				puts("86");
			}
			else puts("-1");
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值