2019.04.13【NOIP提高组】模拟B 组 推公式+推公式+dfs+DP

0 生成树(count.pas/cpp)

有一种图形叫做五角形圈。一个五角形圈的中心有1个由n个顶点和n条边组成的圈。在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形。这些五角形只在五角形圈的中心的圈上有公共的顶点。如图0所示是一个4-五角形圈。

现在给定一个n五角形圈,你的任务就是求出n五角形圈的不同生成树的数目。还记得什么是图的生成树吗?一个图的生成树是保留原图的所有顶点以及顶点的数目减去一这么多条边,从而生成的一棵树。

注意:在给定的n五角形圈中所有顶点均视为不同的顶点。
  在这里插入图片描述

经过一系列艰难的奋斗,我们发现答案即 5 n − 1 ∗ n ∗ 4 5^{n-1}*n*4 5n1n4
因为就是每个五边形去掉一条边,有一个五边形需要去掉两条边

#include <cstdio> 

int t,n;
int d[103];

int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d",&t);
	d[0]=1;
	for (int i=1;i<100;i++)
    	d[i]=(d[i-1]*5)%2007;
	for (;t;t--){
		scanf("%d",&n);
		printf("%d\n",(d[n-1]*n*4)%2007);
	} 
}

1 三核苷酸(tri.pas/.cpp)

三核苷酸是组成DNA序列的基本片段。具体来说,核苷酸一共有4种,分别用’A’,’G’,’C’,’T’来表示。而三核苷酸就是由3个核苷酸排列而成的DNA片段。三核苷酸一共有64种,分别是’AAA’,’AAG’,…,’GGG’。给定一个长度为L的DNA序列,一共可以分辨出(L-2)个三核苷酸。现在我们想用一些统计学的方法来进行一些分析,步骤如下: 1.对于这(L-2)个三核苷酸,我们从左到右给予编号,分别为1到L-2。
2.从这(L-2)个三核苷酸挑选一对出来,一共有(L-2)*(L-3)/2种可能。如果某一对三核苷酸是一样的,我们就记录他们之间的距离。他们之间的距离定义为他们的编号之差。
3.根据我们所记录的“样本数据”,我们现在需要计算样本数据的方差。方差的计算公式是S2=[(x1-X) 2+(x2-X) 2+…+(xn-X)2]/n, X=(x1+x2+…+xn)/n。如果样本的大小n=0,那么我们认为S2=X=0。
例如,我们要统计DNA序列’ATATATA’: 1. 为三核苷酸编号. L1: ATA, L2:TAT, L3:ATA, L4:TAT, L5:ATA.
2. (L1,L3)=2, (L1,L5)=4, (L3,L5)=2, (L2,L4)=2. 所以样本数据是2,4,2,2.
3. 样本数据平均值X=(2+4+2+2)/4=2.5.方差S2=[(2-2.5)2+(4-2.5) 2+(2-2.5)2+(2-2.5)2]/4=0.75.
给定一个DNA序列,请你计算出它的方差。

输入包含多组测试数据。第一行包含一个正整数T,表示测试数据数目。每组数据包含一个由’A’,’G’,’C’,’T’组成的字符串,代表要统计的DNA序列。DNA序列的长度大于等于3且不会超过100000。

展开方差公式,然后=在这里插入图片描述

n即样本数,可以用每种样本数*(每种样本数-1)/2相加得到
把DNA序列转成数字,不用挨个求出样本
然后辛苦推公式吧

#include <cstdio>
#include <cstring>

using namespace std;

const int N=100005;
int t;
long long a[N],b[N],cnt[500],s1[N],s[N],sum[N],s2[N],n,S,S2;

int main(){
	freopen("tri.in","r",stdin);
	freopen("tri.out","w",stdout);
	scanf("%d",&t);
	for (;t;t--){
		char ch;
		n=1;
		ch=getchar();
		while ((ch!='A')&&(ch!='G')&&(ch!='C')&&(ch!='T')) ch=getchar();
		while ((ch=='A')||(ch=='G')||(ch=='C')||(ch=='T')){
			if (ch=='A') a[n++]=1;
			if (ch=='G') a[n++]=2;
			if (ch=='C') a[n++]=3;
			if (ch=='T') a[n++]=4;
			ch=getchar();
		}
		n-=1;
		for (int i=1;i<=n-2;i++){
			b[i]=a[i]*100+a[i+1]*10+a[i+2];
		}
		for (int i=111;i<=444;i++)
			sum[i]=s[i]=s1[i]=s2[i]=cnt[i]=0;
		for (int i=1;i<=n-2;i++){
			s[b[i]]+=cnt[b[i]]*i*i+s1[b[i]]-2*s2[b[i]]*i;
			sum[b[i]]+=cnt[b[i]]*i-s2[b[i]];
			s1[b[i]]+=(long long)i*i;
			s2[b[i]]+=i;
			cnt[b[i]]++;
		}
		S=S2=n=0;
		for (int i=111;i<=444;i++){
			S+=sum[i];
			S2+=s[i];
			n+=cnt[i]*(cnt[i]-1)/2;
		}
		if (n==0) printf("0.000000\n"); else{ 
			double ans=(1.0*S2/n)-(1.0*S/n)*(1.0*S/n);
			printf("%.6f\n",ans);
		} 
	}
}

2 简单数迷(kakuro.pas/.cpp)

很多人都曾经听说过数独,但你是否听说过数谜(Karuro)呢?实际上,数谜是数独的更大(且更难)的兄弟问题,而且在日本也是非常受欢迎的。
数谜问题和填字游戏类似,不过它要填的不是文字而是数字。数谜游戏的目标是用1-9填满所有空格,且这些数字相加的和满足相应的要求(或者称为“提示”),且在同一栏(“栏”是指一些水平或者竖直的连续的空格,用于提示的格子不算空格)不能填重复的数字。当所有格子按要求被填满后,这个数谜就看作被解决了。图1和图2是一个可能的数谜游戏示例。
当然,直接求解数谜问题的话会比较困难。所以现在我们需要解决的是一个更简单的数谜问题。简单数谜的形状是一个(n+1)行乘(m+1)列的矩形。而简单数谜也只有两种要求,就是行要求和列要求,且分别处于第一行和第一列,其他格子则是空格,而左上角是忽略不计的。coolzzz同学爱好简单数谜,他已经给一些简单数谜填好了其中的一些空格。现在,他想寻求你的帮助,来帮他完成这些简单数谜。如图3所示,2和9是coolzzz同学已经填好的空格,图4则是一个基于图3 的一个可能的解答。
在这里插入图片描述
 输入包含多组测试数据。第一行包含一个正整数T,表示测试数据数目。每组数据第一行是n(n<10)和m(m<10),表示数谜的形状的大小。接下来一行有n个整数,是相应的行要求;然后一行是m个整数,是相应的列要求。接下来的n行每行有m个小于10的非负整数,0表示该空格还没有被填数字,其他表示coolzzz同学已经填好的数字。输入数据保证未填数字的空格不会超过16个。

搜索,剪枝,过了(?)

#include <cstdio>
#include <cstring>

using namespace std;

int t,n,m,ans;
int tx[20],ty[20];
int a[20][20],f[5][20][20];
int b[20];
int bz[20][20],fz[20][20];

void dfs(int i,int j,int s){
	if (j>m){
		if (i<n) if(s==tx[i]) {
			dfs(i+1,1,0); 
		}else return; else{
			if (s!=tx[i]) return;
			for (int l=1;l<=m;l++)
			if (b[l]!=ty[l]) return;
			ans++;
			return;
		}
	}
	if (a[i][j]!=0){
		if (s+a[i][j]<=tx[i])
		dfs(i,j+1,s+a[i][j]);
		return;
	}
	for (int l=1;l<=9;l++){
		if ((s+l<=tx[i])&&(b[j]+l<=ty[j])&&(!bz[i][l])&&(!fz[j][l])){
			b[j]+=l;
			bz[i][l]=1;
			fz[j][l]=1;
			f[ans][i][j]=l;
			dfs(i,j+1,s+l);
			if (ans>1) return;
			bz[i][l]=0;
			fz[j][l]=0;
			b[j]-=l;
		}
	}
}

int main(){
	freopen("kakuro.in","r",stdin);
	freopen("kakuro.out","w",stdout);
	scanf("%d",&t);
	for (;t;t--){
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++)
			scanf("%d",&tx[i]);
		for (int i=1;i<=m;i++)
			scanf("%d",&ty[i]);
		ans=0;
		memset(b,0,sizeof b);
		memset(f,0,sizeof f);
		memset(a,0,sizeof a);
		memset(fz,0,sizeof fz);
		memset(bz,0,sizeof bz);
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++){
				scanf("%d",&a[i][j]);
				f[ans][i][j]=a[i][j];
				b[j]+=a[i][j];
				bz[i][a[i][j]]=1;
				fz[j][a[i][j]]=1;
			}		
		dfs(1,1,0);
		if (ans==0) printf("No answer.\n"); else
		if (ans>1) printf("Not unique.\n"); else
		for (int i=1;i<=n;i++){
			for (int j=1;j<=m;j++)
				printf("%d ",f[0][i][j]);
			printf("\n");
		}
	}
}

3 股票投资(stock.pas/.cpp)

很多人都梦想成为股神,但是股票投资是需要一定基本功的。熟知股票买卖的规则就是其中一件很重要的事情。顾名思义,股票的单位是“股”;但是实际上,在公开交易市场,股票交易的单位是“手”,1手股票等于100股股票。另外,和其他商业买卖一样,股票交易是要纳税的。股票交易的税有两种,一种是交易税,一种是印花税。假设当前股价是P,你要买或者卖X手股票,那么你需要付出或者得到的钱是G=P100X。与此同时,无论买或者买,你都必须支付交易税和印花税。印花税有一个固定税率T,例如0.001,则你所需支付的印花税为GT。交易税类似印花税,也有一个固定税率S1,但同时也有一个最低税额S2,这意味着,如果GS1<S2,则你依然需要支付S2的税款,即你需要缴纳的税款为 max(G*S1,S2)。 好了,懂得这个规则,现在你可以小试牛刀了。不过,股票市场更需要的是投资策略。问题来了,假设某个时刻T0,你拥有C元现金;接下来的n秒钟,T1,T2,…,Tn,你都清楚知道某一只股票的价格Pi。在这n秒钟,你都可以任意买入或者卖出或者持有这只股票,当然,无论什么时候,你必须保证你的现金不能出现负数,你也不可能卖出比你持有数量更多的股票。同时T,S1,S2也是已知的。在这样的情况下,第n秒结束后,你可以拥有的现金最多会是多少?

“可以发现这题不可能像真实买股票一样,如果你可以买2手,那就不可能只买1手。同理,如果现在有2手,那就不可能卖1手,因此40分的dp做法很显然大部分都在做一些不可能更新答案的运算,为了优化这个做法 我们可以只需要记录股票为0和最多股票的情况,所以设f[i]为第i秒最多的钱,g[i]为第i秒能有最多的股票,r[i]为第i秒在有最多股票的同时能剩多少钱。
初值f[0]=c;
转移时发现他们可以互相更新,最多的钱可以由最多的股票卖掉得到,最多的股票可以由最多的钱买入得到,当然都可以选择继续持有。
所以得到dp式,f[i]为max{f[i-1],g[i-1]100a[i]+r[i-1]-手续费}
转移g[i]和r[i]时,先设d为由最多的钱可以买多少股票,首先得到d=f[i-1]/100/a[i],但是我们没有考虑手续费的问题,有的时候d手股票加上它的手续费也许会大于f[i-1],所以这种情况下,d要-1,这个操作不止一次!如果那么(g[i-1]<d) 或 (g[i-1]=d且用d买进股票时剩余的钱大于r[i-1]) 那么买d手股票,更行g[i]=d,r[i]=f[i-1]-d*a[i]*100。否则g[i]和r[i]等于g[i-1]和r[i-1]
最后答案就是f[n],时间复杂度O(n)。”

#include <cstdio> 
#include <algorithm>

using namespace std;

const int N=30004;
int t,n,d;
double c,s1,s2,t0,v;
double w[N],f,r,m;

int main(){
	freopen("stock.in","r",stdin);
	freopen("stock.out","w",stdout);
	scanf("%d",&t);
	for (;t;t--){
		scanf("%lf%lf%lf%lf",&c,&s2,&t0,&s1);
		scanf("%d",&n);
		for (int i=1;i<=n;i++){
			scanf("%lf",&w[i]);
			w[i]*=100;
		}
		m=r=c;f=0;
		for (int i=1;i<=n;i++){
			v=m;
			m=max(m,f*w[i]-f*w[i]*s1-max(t0,f*w[i]*s2)+r);
			d=v/w[i]/1;
			while (v-1.0*d*w[i]-1.0*d*w[i]*s1-max(t0,1.0*d*w[i]*s2)<0) d--;
			if ((f<d)||((f==d)&&(v-1.0*d*w[i]-1.0*d*w[i]*s1-max(t0,1.0*d*w[i]*s2)>r))){
				f=d; 
				r=v-1.0*d*w[i]-1.0*d*w[i]*s1-max(t0,1.0*d*w[i]*s2);
			}
		}
		printf("%.3lf\n",m);
	}
}

“君有疾否?”“相思成疾。”

君有疾否=你有病吗
o( ̄︶ ̄)o
相思成疾也是真的强,这一波秀的我

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值