xtu 1233 Coins && zoj 3747 Attack on Titans



xtu
题目意思:

有n个硬币排成一排,求至少连续m个硬币正面朝上的方案数。

给定一个序列,正面朝上硬币连续值范围【0--n】

法一:

一般至少转换成至多,总方案数(2^n)- 至多(m-1)个硬币正面朝上 = 至少m个硬币正面朝上

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long LL;
const int M = 1e9+7;
LL d[1000010][2];
LL a[1000010];
int main()
{
	int i,j,k,n,m,t;
	a[0] = 1;
	d[0][1] = 1;
	for(i=1;i<=1000000;i++){
		a[i] = (a[i-1]*2)%M ;
	}
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&m,&n);
		for(i=1;i<n;i++){
			d[i][1] = a[i];   //不满足条件 
			d[i][0] = 0;  //满足条件 
		}	
		d[n][0] = 1;
		d[n][1] = (a[n]-d[n][0]+M)%M;
//		printf("%d\n",d[n][1]);
		for(i=n+1;i<=m;i++){
			d[i][0] = (d[i-1][0]*2 + d[i-1-n][1])%M;
			d[i][1] = (a[i] - d[i][0] +M)%M;
		}
//		for(i=1;i<=m;i++){
//			printf("%d:%d %d\n",i,(int)d[i][0],(int)d[i][1]);
//		}
		printf("%I64d\n",d[m][0]);
	}               
	return 0;
}

法二:思路完全不同法一,值得借鉴:

d【i】【0】就代表满足条件(>=n)个连续的正面朝上的硬币

d【i】【1】就代表不满足满足条件(>=n)个连续的正面朝上的硬币

当i>n

d[i][0] = d[i-1][0]*2 + d[i-1-n][0];  当前i-1个数满大于等于n个连续的正面朝上的硬币,那么无论第i枚硬币如何都满足条件。但是这种情况下如果第i枚硬币是正面朝上,是在以前(i-1)状态下,所以没有刚好后n枚硬币正面朝上的情况,所以再加上d(i-1-n),为什么还要减一了,因为第i-n枚硬币必须为反面,这样才满足刚好后n枚硬币正面朝上的情况。

“-----”代表之前理解有误。

第i个数如果正面朝上没有给整个串实质性变化,本来满足的满足,不满足的还是不满足,因此有这么一种情况虽然(i-1)不满足,但是正好第i位来了个正面,才满足。

就是后(i-1-n)是后n位正面朝上,后n+1位反面朝上,这样后(n+1)状态确定,前(i-1-n)位为不合法状态。

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long LL;
const int M = 1e9+7;
int n,m,u;
int pw(int k){    //2^k
	LL ans = 1;
	LL a = 2;
	while(k){
		if(k&1) ans=(ans*a)%M; 
		a = (a * a)%M;
		k/=2;
	}
	return ans;
}
LL d[1000010][5];
int solve(){
	int i,j;
	LL ans;
	d[0][1]=1;d[0][0]=0;
//	if(u+1==1)
//	d[1][1] = 0;
	for(i=1;i<=n;i++){
		d[i][0] = ans = (d[i-1][0]+d[i-1][1])%M;
		
		if(i<=u)
		d[i][1] = ans;
		
		else if(i==u+1)
		d[i][1] =(ans-1+M)%M;
		
		else{
			d[i][1] = (ans - d[i-u-1][0]+M)%M;
		}
	} 
	return (d[n][0]+d[n][1])%M ;
} 
int main()
{
	int t,i,j,k;
//	freopen("a.txt","r+",stdin);
//	freopen("rb.txt","w+",stdout);
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		u = m - 1 ; 
		printf("%d\n",solve());
		printf("%d\n",(pw(n)-solve()+M)%M);
	}
	return 0;
}



zoj
题目意思:

给n个士兵排队,每个士兵三种G、R、P可选,求至少有m个连续G士兵,最多有k个连续R士兵的排列的种数。


思路更上题差不多,我们把至多k个连续R士兵条件下,至多n个G 减去 至多m-1个G,就为该条件下至少m个连续G个士兵

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
int n,m,k;
int dg,dr;
int u,v;
typedef long long LL;
const int mod = 1000000007; 
LL d[1000010][4];  //d[i][0]代表第i位为G,不超过(n-m)个G, 不超过k个 R  
//d[i][1]代表第i位为R,不超过(n-m)个G, 不超过k个 R 
//d[i][0]代表第i位为P,不超过(n-m)个G, 不超过k个 R 
long long  fun(){
	int i ;
	long long tmp;
	//d[1][0]=d[1][1]=d[1][2]=1;
	d[0][0] = 1; d[0][2] = 0;  d[0][1] = 0;    //初始化有点妙 cl 
	for(i=1;i<=n;i++){
		tmp = (d[i-1][0] +d[i-1][1] +d[i-1][2])%mod;
		if(i<=dg)
		d[i][0] = tmp;
		else if(i==dg+1)
		d[i][0] = (tmp-1 )%mod;  //第一次出现不满足条件,就是全为G,其方案数为1 
		else d[i][0] = (tmp - d[i-dg-1][1] -d[i-dg-1][2]+2*mod)%mod;  //因为不满足条件为>dg个连续的字母,所以不合法的情况为后dg+1全为G,第i-dg-1为不为G  !!难点 
		
		if(i<=dr)
		d[i][1] = tmp;
		else if(i==dr+1)
		d[i][1] = (tmp-1+mod)%mod;
		else d[i][1] = (tmp - d[i-dr-1][0] -d[i-dr-1][2]+2*mod)%mod;
		
		d[i][2] = tmp;
	} 
	return (d[n][0]+d[n][1]+d[n][2])%mod;
}
int main()
{
	 int i,j;
	 LL ans1,ans2,ans;
	 while(~scanf("%d%d%d",&n,&m,&k)){
		dg = n;dr = k;
		ans1 = fun();
		dg=m-1,dr=k;
		ans2 = fun();
		printf("%d\n",(ans1-ans2+mod)%mod);
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值