麻将
作者:互联网
来源:第四届图灵杯趣味网络邀请赛
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