2017暑假第二阶段第八场 总结

来源:互联网 发布:淘宝怎么追加差评 编辑:程序博客网 时间:2024/06/05 13:23

T1 回文数

问题描述

给你一个数N,求出最小的B(B>=2),使得 N在 B进制下为回文数。

输入格式

第一行1个整数TEST,表示数据组数。 接下来TEST行,每行一个整数N。

输出格式

共输出TEST行,每行对应一个答案B

数据范围

30%的数据 TEST<=10,N<=104
100%的数据 TEST<=1,000,N<=1010


比较常见的思路是把原数写成ak1Bk1+ak2Bk2++a1B1+a0B0的形式,然后就做不起了,只能暴力转进制搞了。

不妨换一种考虑的方向。考虑原数转换为B进制之后的位数,从而优雅地暴力。

首先转换后只有一位数肯定不是最优解,因为这样的话B满足B>N,然而在B=N-1时,一定是两位的回文数(11)N1(对于数字2除外,因为并没有一进制)。

如果转换后是两位数,那么一定是(AA)B的形式,那么满足N=A×B+A=A(B+1),那么从大到小枚举因数A即可。

如果转换后是三位数,那么最小也是(101)B,所以有NB2+1,根据N的范围,B最大仅为105,大于三位数之后B的范围肯定更小,在这个数据范围暴力枚举是可以接受的。

于是得出解法:首先在N以内暴力寻找是否有这样的B;如果找到就停止,如果没找到就枚举N的因数。但是要注意,这样的因数必须要大于N才是符合要求的,因为N以内的B已被枚举过,且被验证为不满足要求。

#include<stdio.h>#include<cmath>#define ll long longusing namespace std;ll N;int a[65];bool check(int b){    if(!N)return true;    int cnt=0,i;    ll tmp=N;    while(tmp)    {        a[++cnt]=tmp%b;        tmp/=b;    }    for(i=1;i<=cnt-i+1;i++)if(a[i]!=a[cnt-i+1])return false;    return true;}int main(){    int T;    ll i,tmp;    bool flag;    scanf("%d",&T);    while(T--)    {        flag=false;        scanf("%lld",&N);        if(N==2){puts("3");continue;}        tmp=ceil(sqrt(N));        for(i=2;i<=tmp;i++)        {            if(check(i))            {                flag=true;                printf("%lld\n",i);                break;            }        }        if(flag)continue;        for(i=tmp-1;i>=1;i--)        {            if(N%i==0&&(N/i-1)>tmp)            {                printf("%lld\n",N/i-1);                break;            }        }    }}

T2 奶牛阵列

问题描述

每天早晨约翰的奶牛都会在挤奶的时候排成阵列,即站成R(1<=R<=10000)行C(1<=C<=75)列的矩阵。我们知道,约翰是奶牛专家,他打算写一本关于喂养奶牛的书,他发现,当奶牛按不同血统标记以后,整个大矩阵就像由很多小矩阵无缝拼接的一样。

请帮助约翰找到面积最小的模型矩阵,使他能拼出整个大矩阵,当然,模型矩阵的尺寸不一定能整除大矩阵,也就是说你可以用若干个模型矩阵,拼出一个包含大矩阵的更大的矩阵。

输入格式

第一行, 两个整数R和C
接下来是由大写字母构成的R*C的矩阵

输出格式

一个整数,表示最小模型矩阵的面积。

样例输入

2 5
ABABA
BABAB

样例输出

4

样例说明:

模型矩阵如下:
AB
BA
拼出的大矩阵如下:
ABABAB
BABABA


根据题意,应该是不能把小矩形重叠放置的。但是由于本题数据太弱,很多错解都能AC。对于错解,这里不一一列举,正解是KMP。

不妨先把每一列考虑成一个字符,再对这些“字符”通过KMP找出“最小循环节”,即最小循环矩形,并记录长度;再把每一行也考虑成一个字符,并进行相同的操作。那么答案就是两个“长度”的乘积。KMP找最小循环矩形的正确性是显然的,求答案的操作可以通过下面的图理解:

这里写图片描述

将字符串转换为“字符”考虑后,我们仅关心两个“字符”是否相同,那么可以使用hash。如果害怕hash出错,也可以开两个hash表。但是由于数据本身太弱,这里就只开了一个。

#include<stdio.h>#define ll long long#define mod 0xffffint R,C,fail[10005],rans,cans;ll Hash[10005];char ch[10005][80];int main(){    int i,j,sd=131;    ll h;    scanf("%d%d",&R,&C);    for(i=1;i<=R;i++)scanf("%s",&ch[i][1]);    for(i=1;i<=R;i++)    {        h=0;        for(j=1;j<=C;j++)h=(h+ch[i][j])*sd%mod;        Hash[i]=h;    }    fail[1]=j=0;    for(i=2;i<=R;i++)    {        while(j>0&&Hash[j+1]!=Hash[i])j=fail[j];        if(Hash[j+1]==Hash[i])j++;        fail[i]=j;    }    rans=R-fail[R];    for(i=1;i<=C;i++)    {        h=0;        for(j=1;j<=R;j++)h=(h+ch[j][i])*sd%mod;        Hash[i]=h;    }    fail[1]=j=0;    for(i=2;i<=C;i++)    {        while(j>0&&Hash[j+1]!=Hash[i])j=fail[j];        if(Hash[j+1]==Hash[i])j++;        fail[i]=j;    }    cans=C-fail[C];    printf("%d",cans*rans);}

提供几组肉眼就能看出答案的小数据,能够卡掉很多AC代码。

/*data1: 3 9 CBABCCBBC CACBAACCA BACCCAABB 正解27 data2: 2 6 BACBAC CBCBAC 正解12 data3: 5 7 BAAAABB ABABABA ABABAAB BAAAAAA BBABABB 正解35 data4: 2 8 AAABCAAA ABABABAB 正解12 */

T3 突击队

问题描述

何老板的公司有t名员工,编号1到t.
何老板将他们分成了n组,每组员工的编号都是连续的,比如第i组[Ai,Bi]表示编号Ai,Ai+1,…,Bi都属于该组。
现何老板接到一个大单,工期很紧,需要组成一个突击组来完成任务,他想在每个组里抽出一些员工来组成突击组。现在告诉你每个组至少要抽出的人数,问,该突击组的人数最少是多少?

输入格式

第一行,一个整数n
接下来n行,每行三个整数Ai,Bi,Ci,描述一个组的情况,其中Ai,Bi表示该组员工编号的范围,Ci表示改组中至少要抽出Ci个人去突击组。

输出格式

一个整数,表示所求的答案。

数据范围

0 <= t <= 50000
1 <= n <= 50000
ai <= bi
1 <= ci <= bi - ai+1


方向对了就是道水题,如果不对什么错解都能想出来。

正解是差分约束。所有的区间的数据都可以看成前缀和相减的一个不等式。由于一个编号的人一定有一个,不会存在负数个人(废话),此外的约束条件还有:0sumisumi11

一开始还想写个Dijkstra+Heap,结果忘了Dijkstra不能处理负权边……

#include<stdio.h>#include<queue>#include<cstring>#define Max(x,y) ((x>y)?(x):(y))#define MAXN 50005#define MAXM 150005using namespace std;int N,T;int en[MAXM],nex[MAXM],las[MAXN],len[MAXM],tot;void ADD(int x,int y,int z){    en[++tot]=y;    nex[tot]=las[x];    las[x]=tot;    len[tot]=z;}int dis[MAXN];bool mark[MAXN];void SPFA(int s){    int i,x,y;    memset(dis,-60,sizeof(dis));    queue<int>Q;    Q.push(s);    dis[s]=0;    while(Q.size())    {        x=Q.front();Q.pop();mark[x]=false;        for(i=las[x];i;i=nex[i])        {            y=en[i];            if(dis[y]<dis[x]+len[i])            {                dis[y]=dis[x]+len[i];                if(!mark[y])mark[y]=true,Q.push(y);            }        }    }}int main(){    int i,x,y,z;    scanf("%d",&N);    for(i=1;i<=N;i++)    {        scanf("%d%d%d",&x,&y,&z);        ADD(x-1,y,z);        T=Max(T,y);    }    for(i=1;i<=T;i++)    {        ADD(i,i-1,-1);        ADD(i-1,i,0);    }    SPFA(0);    printf("%d",dis[T]);}

总结

这次考试又很爆炸,只不过收获很大,至少暴露了很多知识点的不熟悉。

T1打表找规律太久,结果发现压根就没有规律,要学会更优雅的暴力解法,思考问题的角度也要多样化,不能江化;T2正解思考的方式也很值得借鉴,KMP算法还要加强;T3主要是提醒了差分约束这种算法,太久没有用了,确实错得应该。

原创粉丝点击