其他分享
首页 > 其他分享> > 麻将

麻将

作者:互联网

来源:第四届图灵杯趣味网络邀请赛
https://xjoi.net/contest/4228
https://contest.xinyoudui.com/statements/22a/b21510ada25e/statement_zh.html

有n种牌,编号从1到n,第i种牌有a[i]张。

给定常数x和y,问能否把这些牌分成若干组,每组满足下列条件之一:

刻子:包含x张编号相同的牌。 顺子:包含y张编号不同但连续的牌。

\(1<=n<=10^3,1<=x,y<=10^9\)

引理:如果能够成功分组,一定存在一种合法的分组方案使得编号最小的牌尽可能地组成刻子。

比如\({n=2x+1}\),考虑两种分法,分成2个刻子1个顺子 或 1个刻子x+1个顺子。

考虑每个顺子都是从编号1到y,x+1个顺子其实也就相当于编号1到y每个都组成一个刻子,然后总体是一个顺子。

所以所有方案其实都可以转化成刻子最多的那种方案。从前往后,重复子问题。

//模拟 O(N^2)
bool solve(){
	for(int i=1;i<=n-y+1;i++){
		a[i] %= x;
		for(int j=i+1;j<i+y;j++){
			if(a[j] < a[i]) return false;
			a[j] -= a[i];
		}
	}
	for(int i=n-y+2;i<=n;i++)
		if(a[i] % x) return false;
	return true;
}
int main(){
	cin>>n>>x>>y;
	for(int i=1;i<=n;i++) cin>>a[i];
	if(solve()) puts("Yes");
	else puts("No");
	return 0;
}

//差分优化 O(N) 一次可以直接修改整个范围
bool solve(){
	int tmp = 0;
	for(int i=1;i<=n-y+1;i++){
		tmp += b[i];//此时tmp即为a[i] 
		if(tmp < 0) return false;
		int r = tmp % x;//全做成刻子后剩下的 
		//相当于a[i]~a[i+y-1]每个数都减去了r 
		tmp -= r;
		b[i+y] += r;
	}
	for(int i=n-y+2;i<=n;i++){
		tmp += b[i];
		if(tmp < 0) return false;
		if(tmp % x) return false;
	}
	return true;
}
int main(){
	cin>>n>>x>>y;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i] = a[i] - a[i-1];
	}
	if(solve()) puts("Yes");
	else puts("No");
	return 0;
} 

标签:刻子,puts,int,solve,麻将,编号,顺子
来源: https://www.cnblogs.com/dtdbm/p/16294999.html