17.8.17第八次测试
1. 鼎纹
【问题描述】 据说鼎纹图案的制造是铜模印出来的,这是我国古代劳动人民智慧的结晶。铜模印过的地方,会留下深深的印记,经过时间的炼化,洗练成历史的遗存。 聪明的古代劳动人民拥有一个 a 行 b 列的铜模,每个位置要么是 0(代表 这个点是平的),要么是 1(代表这个点是凸起的)。他们想造 个 n 行 m 列的鼎 ,其中每个位置也都是 0 或 1,表示经过若干次印后,每个位置的结果。 有一些要求。铜模是不能旋转和翻转的;在印的过程当中,铜模的凸起不 能出现在鼎面的外面(平的部分是可以出现在外面的),鼎面上的同一个位置不能被多个凸起印下(在任意两次印时,鼎面上不存在一个点,使得这两次都有铜模上为 1 的点覆盖它)。 请你判断这个鼎面能不能被印出来。 输入文件件名为 grain.in。 本题多测。 第一行,一个整数 T ,表示测试 点数量。接下来 T 个测试点,每 个测试点中: 第一行包含 4 个整数 n,m,a,b。 接下来 n 行 ,每行 m 个字符,描述鼎面 。“0”表示 平,“1”表示凸起。接下来 a 行,每行 b 个字符, 描述铜模。“0”表示平,“1”表示凸起。 输出文件名为 grain.out。 共有 T 行 ,对于每个测试点,输出 “YES”(能)或“NO”(不能)。 测试数据: grain.in 2 3 4 4 2 1100 0110 1100 10 01 10 00 2 2 2 2 11 11 01 10 grain.out YES NO 对于 70% 的数据,n,m,a, b <= 50。 对于 100% 的数据,1 <= T <=10; 1<=n,m, a, b <= 1000。
题解:大模拟啊这题,没啥特别的算法。输入模板的时候存下来每一个点和模板最左上角那个点的相对位置,然后遍历鼎面,如果为1,就从这点开始,把和它相对位置和之前模板相对位置一样的点全部置为0,若超出边界或者已经为0什么的就输出no,若遍历完成就是yes。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct kk{ int x,y; }k[100010]; char tar[1005][1005]; int n,m,a,b,t,s,tx,ty,sx,sy; char g; int main(){ freopen("grain.in","r",stdin); freopen("grain.out","w",stdout); scanf("%d",&t); while(t--){ int flg=1,cnt=0,ff=1; scanf("%d%d%d%d",&n,&m,&a,&b); for(int i=0;i<n;i++) for(int j=0;j<m;j++) tar[i][j]=0; for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) tar[i][j]=0; for(int i=0;i<n;i++) scanf(" %s",&tar[i]); for(int i=1;i<=a;i++) for(int j=1;j<=b;j++){ scanf(" %1d",&s); if(s){ if(ff==1){ k[++cnt].x=0; k[cnt].y=0; sx=i; sy=j; ff=0; } else{ k[++cnt].x=i-sx; k[cnt].y=j-sy; } } } for(int i=0;i<n;i++){ if(flg==0)break; for(int j=0;j<m;j++){ if(flg==0)break; if(tar[i][j]=='1'){ if(ff==1){ flg=0; break; } for(int p=1;p<=cnt;p++){ tx=i+k[p].x,ty=j+k[p].y; char e=tar[tx][ty]; if(tx>=0&&ty>=0&&tx<n&&ty<m&&(e=='1')){ tar[tx][ty]='0'; } else{ flg=0; break; } } } } } if(flg)printf("YES\n"); else printf("NO\n"); } return 0; }
2. 看球赛
2200 年 里奥迪奥带领的十星巴西对战莱昂纳多带领的阿根廷的世界杯决赛马上开始了!前来在巨型球场观看比赛的观众数不甚数,但是由于突如其来的系统原因,大家不能网上购票,只能到售票窗口,排成长龙买票. 按售票处规定,每一个限购一张门票,且每一张门票 50 美元。 在排成长龙的球迷中有 n 个人手持 50 美元,另外有 n 个人手持 100 美元。假设售票处开始的时候没有零钱,试问这 2n 个球迷有多少种排队方式使得售票处不会出现找不出钱的尴尬局面,导致拖延球迷看球时间。 【输入与输出说明】 输入两行,第一行一个正整数 T,表示数据个数。 第二行有 T 个正整数,n1,n2,....nT. 输出 T 行,每一行为被 10^9+7 模过的结果。 【样例输入】 【样例输出】 1 2 2 2 2 2 5 42 【样例说明】 例如当 n=2 时,用 A 表示手持面值 50 美元的球迷,用 B 表示手持面值为 100 美元的球迷,则最多可得到以下两组不同的排队方式使得售票处不会出现找不出钱的情况。 售票处 A A B B 售票处 A B A B 【数据范围】 对于 10% n <= 10, T <= 10 对于 20% n <= 15, T <= 10 对于 60% n <= 2000, T <= 10 对于其中 20% 2000 < n <= 10^7, T <= 3 对于其中 20% 10^7 < n <= 3*10^7, T = 1
题解:一道数学题。要用卡特兰数的相关知识。当时不会,所以没做出来。
卡特兰数:
卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列。以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为 : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...
令h(0)=1,h(1)=1,catalan数满足递推式 :
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) (n>=2)
例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
另类递推:
h(n)=h(n-1)*(4*n-2)/(n+1);
递推关系的解为:
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推关系的另类解为:
h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
以上来自百度百科。
知乎上有一个对公式推导的文觉得不错,可以看看。
https://www.zhihu.com/question/60677340
没有授权,侵删。
上代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define mod 1000000007 #define ll long long ll ans,n,s1,s2,map[10010],m; int t; ll ksm(ll a,ll b){ ll ret=1,x=a; while(b){ if(b&1)ret=1ll*ret*x%mod; x=x*x%mod; b>>=1; } return ret; } int main(){ freopen("football.in","r",stdin); freopen("football.out","w",stdout); scanf("%d",&t); while(t--){ scanf("%I64d",&n); m=n<<1; s1=s2=1; for(int i=1;i<=n;++i){ s1=s1*(m-i+1)%mod; s2=s2*i%mod; } s2=s2*(n+1)%mod; ans=s1*ksm(s2,mod-2)%mod; printf("%I64d\n",ans); } }
3.靶形数独
【题目描述】 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。 靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。 上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为 9 分,再外面一圈(蓝色区域)每个格子为 8 分,蓝色区域外面一圈(棕色区域)每个格子为 7 分,最外面一圈(白色区域)每个格子为 6 分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。 由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
【输入描述】 一共 9 行。每行 9 个整数(每个数都在 0—9 的范围内),表示一个尚未填满的数独方 格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。 【输出描述 】 输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。 【样例输入 】 【输入输出样例 1】 7 0 0 9 0 0 0 0 1 1 0 0 0 0 5 9 0 0 0 0 0 2 0 0 0 8 0 0 0 5 0 2 0 0 0 3 0 0 0 0 0 0 6 4 8 4 1 3 0 0 0 0 0 0 0 0 7 0 0 2 0 9 0 2 0 1 0 6 0 8 0 4 0 8 0 5 0 4 0 1 2 【输入输出样例 1】 2829 【输入输出样例 2】 0 0 0 7 0 2 4 5 3 9 0 0 0 0 8 0 0 0 7 4 0 0 0 5 0 1 0 1 9 5 0 8 0 0 0 0 0 7 0 0 0 0 0 2 5 0 3 0 5 7 9 1 0 8 0 0 0 6 0 1 0 0 0 0 6 0 9 0 0 0 0 1 0 0 0 0 0 0 0 0 6 【输入输出样例 2】 2852 【数据范围】 40%的数据,数独中非 0 数的个数不少于 30。 80%的数据,数独中非 0 数的个数不少于 26。 100%的数据,数独中非 0 数的个数不少于 24。
题解:和八皇后很像的一道,因为时间4s,所以可以暴力搜索,也没啥技术含量,完全考暴力。遍历每个点可能情况,若填满就更新得分就行。
代码:
#include<cstdio> #include<cstring> #include<algorithm> #define searchnext(x,y) y==9? search(x+1,1):search(x,y+1) using namespace std; int get[11][11][11]; bool r[10][10],l[10][10],s[10][10]; int a[10][10],b[10][10]; int ans=-1,sum; int cul(int x,int y,int k) { if(x==5&&y==5)return 10*k; else if(x>=4&&x<=6&&y>=4&&y<=6)return 9*k; else if(x>=3&&x<=7&&y>=3&&y<=7)return 8*k; else if(x>=2&&x<=8&&y>=2&&y<=8)return 7*k; else return 6*k; } bool fill(int x,int y,int k){ if(r[x][k]||l[y][k]||s[(x-1)/3*3+(y-1)/3][k])return 0; b[x][y]=k; r[x][k]=l[y][k]=s[(x-1)/3*3+(y-1)/3][k]=1; sum+=get[x][y][k]; return 1; } void del(int x,int y,int k) { r[x][k]=l[y][k]=s[(x-1)/3*3+(y-1)/3][k]=0; } void search(int x,int y){ if(x==10){ ans=max(ans,sum); return; } if(b[x][y])searchnext(x,y); else { for(int i=1;i<=9;i++) { int t=sum; if(fill(x,y,i)) { searchnext(x,y); del(x,y,i); sum=t; } } b[x][y]=0; } } int main(){ freopen("sudoku.in","r",stdin); freopen("sudoku.out","w",stdout); for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) for(int k=1;k<=9;k++) get[i][j][k]=cul(i,j,k); for(int i=9;i>0;i--) for(int j=9;j>0;j--){ scanf("%d",&a[i][j]); if(a[i][j])fill(i,j,a[i][j]); } search(1,1); printf("%d",ans); return 0; }