小 X 分砖块
(brick.pas/c/cpp)
问题描述
小 X 喜欢跟着爸爸跑到建筑工地上去。
这天,小 X 看到一排砖,每块要么是白色的(0),要么是黑色的(1)。小 X 想把这排
砖分成若干非空段,使得每段白砖和黑砖块数的比例相同。
当然,小 X 可以直接把整排砖作为一段,那就太简单了。为了增加难度,小 X 想知道最
多能分成多少段,例如:
100011 = 10 + 0011(即样例 1,最多分成 2 段,比例为 1:1);
0001110000000001 = 0001 + 11000000 + 0001(即样例 2,最多分成 3 段,比例为 3:1) 。
小 X 百思不得其解,希望你帮帮他。
输入格式
第一行包含一个整数 N。我们将用 N 行来描述这排砖,初始时这排砖为空。
接下来 N 行,每行包含用一个空格隔开的两个整数 Ki , Ci ( Ci 只可能是 0 或 1),表示
在上一行描述完后尾部又有了 Ki 块颜色为 Ci 的砖。
输出格式
第一行包含一个整数,表示最多能分成的段数。
样例数据1
输入
3 1
1
3 0
2 1
输出
2
样例数据2
输入
4 3
0
3 1
9 0
1 1
输出
3
【数据范围】
对于 30%的数据,N=1。
对于 60%的数据,所有 Ki 均相等。
对于 100%的数据,1≤N≤100000,1≤ Ki ≤1000000000,砖的总块数不超过 1000000000。
分析:从给数据的方式和数据大小可以看出,我们只需读入所有数据存在数组里,然后for循环排查就可以了。考试中,我用了一个特别绕的方法,就是真的照着题目模拟,建三个临时数组(这一段的某颜色砖块总数、与最简整数比的余数、是最简整数比的几倍),每次算到前面的另一种颜色满足整除就判断这种颜色能不能满足,如果满足切一刀,两个数组又重置,反复如此……这样,逻辑的错误很容易出现,结果最后只得了前30%特判的送的分……其实,将指针前面部分看作一个整体,就很好做了,没有什么复杂的数组重置、逻辑判断,我好心疼我的分啊!QAQ
代码
因为我是一个蒟蒻,最讨厌有些人写博客不打注释了,我看着代码还要研究半天……所以,我的注释绝对打足了的:
//一个重要的提示:因为要把砖块切分成颜色比例相同的若干段,即每一段的比例必须等于总数之比
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=100100;
int n,ans;
int a[maxn],b[maxn],s[maxn],t[maxn];
int getint()//读入优化
{
int sum=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
int main()
{
freopen("brick.in","r",stdin);
freopen("brick.out","w",stdout);
n=getint();
for(int i=1;i<=n;++i)
{
a[i]=getint();b[i]=getint();
s[b[i]]+=a[i];
}
if(!s[0]||!s[1])//特判:只有一种颜色的砖,当然是一个一个分
{
printf("%d\n",s[0]+s[1]);
return 0;
}
for(int i=1;i<=n;++i)//相当于将指针之前都看作一个整体,若在指针处能将前面切成与总数之比相同的部分,而上次切的地方之前也满足总数之比,即指针处到前一次之间的砖块比例满足总数之比,那肯定在指针处可以ans++
{
int x=b[i],y=b[i]^1;
if(LL(s[x])*t[y]%s[y]==0)//判断指针前另一种颜色是否满足比例(如:总数之比化简后黑:白=5:3,现在读到的是黑色,那前面所有的白色至少要是3的倍数,不然的话,黑色砖块要切出分数,肯定不满足比例)
{
int z=LL(s[x])*t[y]/s[y]-t[x];//算一算还需要多少个才能和另一种颜色构成总数之比
if(z>=1&&z<=a[i])//现在这一段够加上这么多个(至于为什么要>=1,因为刚开始t数组都是0,也满足整除,所以z会出现负数,需舍去)
++ans;
}
t[x]+=a[i];//更新本颜色在指针之前的总数
}
printf("%d\n",ans);
return 0;
}
终于,我也把我的超级难饶的代码调对了,竟然是因为倍数的精度问题,把倍数数组改成double型就可以了,代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int eps=1e-9;
int n,ans;
int tot[5],zj[5],cnt[5],ys[5];//zj是最简整数比,cnt是此段总数,ys是余数
double bs[5];//bs是倍数
struct node{
int color,num;
}brick[100100];
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
int main()
{
freopen("brick.in","r",stdin);
freopen("brick.out","w",stdout);
n=getint();
if(n==1)//30%
{
ans=getint();
printf("%d\n",ans);
return 0;
}
for(int i=1;i<=n;++i)
{
brick[i].num=getint();
brick[i].color=getint();
tot[brick[i].color]+=brick[i].num;
}
int a=tot[0],b=tot[1],c;
if(aif(b==0)
{
printf("%d\n",a);
return 0;
}
while(a%b!=0)
{
c=a%b;
a=b;
b=c;
}
if(b==1)
{
printf("1\n");
return 0;
}
zj[0]=tot[0]/b;
zj[1]=tot[1]/b;
cnt[brick[1].color]=brick[1].num;
ys[brick[1].color]=cnt[brick[1].color]%zj[brick[1].color];
bs[brick[1].color]=double(cnt[brick[1].color])/zj[brick[1].color];
for(int i=2;i<=n;++i)
{
if(brick[i].color==0)
{
cnt[0]+=brick[i].num;
bs[0]=double(cnt[0])/zj[0];
ys[0]=cnt[0]%zj[0];
if(ys[1]==0&&bs[1]>=1)
{
if(bs[0]-bs[1]>=-eps&&bs[0]-double(brick[i].num)/zj[0]+eps<=bs[1])
{
cnt[0]=cnt[0]-int(bs[1])*zj[0];
cnt[1]=0;
ys[0]=cnt[0]%zj[0];
ys[1]=0;
bs[0]=double(cnt[0])/zj[0];
bs[1]=0;
ans++;
}
}
}
else
{
cnt[1]+=brick[i].num;
bs[1]=double(cnt[1])/zj[1];
ys[1]=cnt[1]%zj[1];
if(ys[0]==0&&bs[0]>=1)
{
if(bs[1]-bs[0]>=-eps&&bs[1]-double(brick[i].num)/zj[1]+eps<=bs[0])
{
cnt[1]=cnt[1]-int(bs[0])*zj[1];
cnt[0]=0;
ys[1]=cnt[1]%zj[1];
ys[0]=0;
bs[1]=double(cnt[1])/zj[1];
bs[0]=0;
ans++;
}
}
}
}
printf("%d\n",ans);
return 0;
}
本题结。