【NOIp2004提高组】食虫算 题解

时间:2022-09-04 00:24:04

所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:

 43#9865#045
+ 8468#6633
44445509678

其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是555和333,第二行的数字是555。

现在,我们对问题做两个限制:

首先,我们只考虑加法的虫食算。这里的加法是\(N\)进制加法,算式中三个数都有\(N\)位,允许有前导的\(0\)。

其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是\(N\)进制的,我们就取英文字母表午的前\(N\)个大写字母来表示这个算式中的\(0\)到\(N−1\)这\(N\)个不同的数字:但是这\(N\)个字母并不一定顺序地代表\(0\)到\(N\)−\(1\)。输入数据保证\(N\)个字母分别至少出现一次。

 BADC
+CBDA
______
DCCC

上面的算式是一个4进制的算式。很显然,我们只要让\(ABCD\)分别代表\(0123\),便可以让这个式子成立了。你的任务是,对于给定的\(N\)进制加法算式,求出\(N\)个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。

输入输出格式

输入格式:

包含四行。

第一行有一个正整数\(N(N \le 26)\)

后面的三行,每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有\(N\)位。

输出格式:

一行,即唯一的那组解。

解是这样表示的:输出\(N\)个数字,分别表示\(…A,B,C,…\)所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。

输入输出样例

输入样例#1:

5
ABCED
BDACE
EBBAA

输出样例#1:

1 0 3 4 2

说明

对于30%的数据,保证有\(N \le 10\)

对于50%的数据,保证有\(N \le 15\)

对于全部的数据,保证有\(N \le 26\)

题目链接

题解

对于这道题,很明显可以总结出它的模型,将问题转化为已知\(N\)元一次方程组以及\(N\)元不等式的求解问题。也就是说可以通过高斯消元来解决。

在这之前,要先知道高斯消元是什么。

答:高斯消元就是加减消元。

对于一个\(N\)元方程组,我们可以将它转化为矩阵乘法的形式,或者一个矩阵的形式。比如说下面这个方程组:

$ x +y = 10\(
\) x - 3y = 6$

可将其转化为:

\[\begin{Bmatrix}1 & 1\\1 & -3\end{Bmatrix}\begin{Bmatrix}x & y\end{Bmatrix} =\begin{Bmatrix}10 \\ 6\end{Bmatrix}(1)
\]

也可转化为

\[\begin{matrix}1 & 1 & 10 \\1 & -3 & 6\end{matrix}
\]

而后,将每一层的方程处理后与下面的方程相加消去一个未知数,一般情况下,当消除第\(i\)层时,消掉第\(i\)个未知数。

这样消元的矩阵呈现出倒三角型, 如图所示:

\[\begin{Bmatrix}a_{11} & a_{12} & a_{13} & a_{14} & ... & a_{1n} \\ a_{21} & a_{22} & a_{23} & a_{24} & ... & a_{2n} \\ ...&...&...&...&...&... \\ a_{n1} &...& ... &...&...& a_{nn} \end{Bmatrix} (2)
\]

\[\begin{Bmatrix}a_{11} & a_{12} & a_{13} & a_{14} & ... & a_{1n} \\ 0 & a_{22} & a_{23} & a_{24} & ... & a_{2n} \\ 0 & 0 & a_{33} & ... & ...&a_{3n} \\...&...&...&...&...&... \\ 0 & 0 & 0 & 0 & ...& a_{nn} \end{Bmatrix}\begin{Bmatrix}c_1 \\ c_2 \\ c_3 \\ ...\\ c_n\end{Bmatrix} = \begin{Bmatrix}b_1 \\ b_2 \\ b_3 \\ ... \\ b_n \end{Bmatrix} (3)
\]

如果不好理解,我们可以先模拟下上述过程, 先举个例子:

1 -2 3 6
4 -5 6 12
7 -8 10 21

在这里要注意一下,我们往往是将这个系数绝对值最大的方程转移到被减的这一行,这样就可以减小误差

所以我们先将矩阵变成这样

7 -8 10 21
4 -5 6 12
1 -2 3 6

然后将方程化简,使要被消去的未知数系数为1, 方便后面消元

1 -8/7 10/7 3
4 -5 6 12
1 -2 3 6

然后与下面的式子加减消元,消去未知数,即系数化为0;

1 -8/7 10/7 3
0 -3/7 2/7 0
0 -6/7 11/7 3

然后这时候第一列就被化简完成

第二第三行同理,最终会被化简为上图\((3)\)式

那么我们可以根据上述的过程模拟出高斯消元代码:

#include<bits/stdc++.h>
const double EPS = 1e-8;
double b[111][111];
int n;
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i++){ //方便起见,我们从0开始编号
for(int j = 0; j < n; j++)
scanf("%lf", &b[i][j]);
scanf("%lf",&b[i][n]);
}
for(int i = 0; i < n; i++){
int pos = i;
for(int j = i; j < n; j++)
if(fabs(b[i][j]-b[pos][i]) <= EPS) pos = j; //调整矩阵
for(int j = 0; j <= n; j++) std::swap(b[i][j], b[pos][j]);
if(fabs(b[i][i]) <= EPS){ printf("No Solution\n"); return 0;}
for(int j = i+1; j <= n; j++) b[i][j] /= b[i][i]; // 化简方程
for(int j = 0; j < n; j++) if(i != j) for(int k = i+1; k <= n; k++) b[j][k] -= b[j][i]*b[i][k]; // 消元
}
for(int i = 0; i < n; i++) printf("%.2lf\n", b[i][n]);
return 0;
}

再回到这道题,就很容易得出解了。

与高斯消元不同的是,这里的解也是未知数,但是未知数的范围是确定的,所以要列出矩阵

根据样例可以列出矩阵:

\[\begin{bmatrix} -1 & 0 & 0 & 1 & 1 \\ -1 & 0 & 1 & 0 & 1 \\ 1 & -1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \\ 1 & 1 & 0 & 0 & -1 \end {bmatrix} \begin{bmatrix} A\\B\\C\\D\\E \end {bmatrix} = \begin{bmatrix} {0\ \ \ \ \ or\ \ \ \ \ n }\\{0\ \ or\ \ -1\ \ or\ \ n-1}\\{0\ \ or\ \ -1\ \ or\ \ n-1}\\{0\ \ or\ \ -1\ \ or\ \ n-1}\\{0\ \ or\ \ -1\ \ or\ \ n-1} \end {bmatrix}
\]

由此可以得出代码:(由于博主比较弱,代码一旦超过50行就会引起不适,故高斯消元部分压了部分行。

#include<bits/stdc++.h>
#define MAXN 26
int n,equ,var,d[MAXN+10],x[MAXN+10];bool vis[MAXN+10];char s[3][MAXN+10];
typedef int matrix[MAXN+10][MAXN+10];matrix a,g;
int gcd(int a,int b) { int t; while(b) {t=a%b;a=b;b=t;} return a;}
bool check() {
memset(vis,0,sizeof vis);
for(int i=1; i<=n; i++) { x[i]=0;
for(int j=1; j<=n; j++) x[i]+=g[i][j]*d[j];
if(x[i]%a[i][i]||x[i]/a[i][i]<0||x[i]/a[i][i]>=n||vis[x[i]/a[i][i]]) return 0;
x[i]/=a[i][i]; vis[x[i]]=1;
}
return 1;
}
void dfs(int i) {
if(i==n) {
if(check()) {
for(int i=1; i<n; i++) printf("%d ",x[i]);
printf("%d\n",x[n]); exit(0);
}return ;
}d[i]=1; dfs(i+1);
d[i]=0; dfs(i+1);
}
int main() {
scanf("%d", &n); scanf("%s%s%s",s[0],s[1],s[2]);
for(int i=0; i<n; i++) {
for(int j=0; j<2; j++) a[n-i][s[j][i]-'A'+1]++;
a[n-i][s[2][i]-'A'+1]--;
}
for(int i=1; i<=n; i++) g[i][i]=n,g[i][i-1]=-1;
g[1][0]=0; equ=var=n;
for(int row=1, col=1; row<=equ&&col<=var; row++, col++) { int mxr=row; // 高斯消元,压行写法, 方便从字符中处理。
for(int i=row+1; i<=equ; i++)
if(abs(a[i][col])>abs(a[mxr][col])) mxr=i;
if(mxr!=row) std::swap(a[row],a[mxr]), std::swap(g[row],g[mxr]);
if(!a[row][col]) {row--;continue;}
for(int i=1; i<=equ; i++)
if(i!=row&&a[i][col]) {
int lcm=a[i][col]/gcd(a[i][col],a[row][col])*a[row][col];
int t1=lcm/a[i][col],t2=lcm/a[row][col];
for(int j=1; j<=var; j++) {
g[i][j]=t1*g[i][j]-t2*g[row][j];
a[i][j]=t1*a[i][j]-t2*a[row][j];
}
}
}
dfs(1);
}

【NOIp2004提高组】食虫算 题解的更多相关文章

  1. &lbrack;NOIP2004&rsqb; 提高组 洛谷P1092 虫食算

    题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 其中# ...

  2. NOIP提高组2004 合并果子题解

    NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消 ...

  3. &lbrack;DP&rsqb;Luogu 2014NOIP提高组 飞扬的小鸟题解

    2014NOIP提高组飞扬的小鸟题解 题目描述 Flappy Bird是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一 ...

  4. NOIP 2008提高组第三题题解by rLq

    啊啊啊啊啊啊今天已经星期三了吗 那么,来一波题解吧 本题地址http://www.luogu.org/problem/show?pid=1006 传纸条 题目描述 小渊和小轩是好朋友也是同班同学,他们 ...

  5. &lbrack;NOIP2004提高组&rsqb;虫食算

    题目:洛谷P1092.codevs1064.Vijos P1099. 题目大意:给你一个$n$进制.每个数都是$n$位的三个数a,b,c,这些数的数位由字母表示(共$n$个字母,从‘A’开始),所有数 ...

  6. noip2004提高组题解

    这次有两道题以前已经做过了,所以分数什么的也没有意义了.发现这年的难度设置极不靠谱,前三题都比较简单,最后一题太难,不知道出题人怎么想的. 第一题:储蓄计划 模拟. 第二题:合并果子 贪心.每次选最小 ...

  7. NOIP提高组历年真题题解

    2018 铺设道路 差分水题,推一下结论就好了. #include<cstdio> #include<algorithm> using namespace std; ],d[] ...

  8. 洛谷 1091 合唱队形&lpar;NOIp2004提高组)

    [题解] 分别做一遍最长上升序列和最长下降序列,再枚举峰的位置计算答案即可. #include<cstdio> #include<algorithm> #include< ...

  9. &lbrack;NOI Online &num;2 提高组&rsqb;涂色游戏 题解

    题目描述 你有 1020 个格子,它们从 0 开始编号,初始时所有格子都还未染色,现在你按如下规则对它们染色: 编号是 p1 倍数的格子(包括 0号格子,下同)染成红色. 编号是 p2 倍数的格子染成 ...

随机推荐

  1. 从游戏脚本语言说起,剖析Mono所搭建的脚本基础

    0x00 前言 在日常的工作中,我偶尔能遇到这样的问题:“为何游戏脚本在现在的游戏开发中变得不可或缺?”.那么这周我就写篇文章从游戏脚本聊起,分析一下游戏脚本因何出现,而mono又能提供怎样的脚本基础 ...

  2. C语言 &&num;183&semi; 打印1-200之间的素数

    素数定义:除了1和本身再无其他整数可被其本身整除的数称为素数,也称质数. 举一例子打印出1-200之间所有的素数: #include<stdio.h> #include<math.h ...

  3. Smarty模板技术学习&lpar;二&rpar;

    本文主要包括以下内容 公共文件引入与继承 内容捕捉 变量调剂器 缓存 Smarty过滤器 数据对象.注册对象 与已有项目结合 公共文件引入与继承 可以把许多模板页面都用到的公共页面放到单独文件里边,通 ...

  4. windows docker测试

    最近测试环节要求比较多,笔记本上虚拟机越来越多,试验一下docker,随笔如下. 一.安装docker 主机windows 10 专业版 网上在windows10上安装docker有两种方法 一个是使 ...

  5. js后退一直停留在当前页面或者禁止后退

    //禁用后退按钮 function stopHistoryGo() { //禁用回退 window.location.hash="no-back-button"; window.l ...

  6. Java&lowbar;spark简单例子

    import org.apache.spark.{SparkContext, SparkConf} /** * Created by spark on 15-1-19. * 根据key对K-V类型的R ...

  7. Linux命令之文件处理

    文件处理命令 1.dirname命令 dirname命令去除文件名中的非目录部分,仅显示与目录有关的内容.dirname命令读取指定路径名保留最后一个/及其后面的字符,删除其他部分,并写结果到标准输出 ...

  8. 插入多行数据的时候,一个insert插入多行

    如:insert into t_users(a,b,c)value('1','2','3'),('3','4','5'),('6','7','8') ('1','2','3'),('3','4','5 ...

  9. flex 输入框布局

    1:创建一个弹性容器(display:flex) 2:构建2个或3个弹性项目. 3:把弹性项目设置为居中对齐.(align-items:center) 4:改变input自身对齐方式,把它设置为拉伸以 ...

  10. 自定义GridView实现分割线解析

    前两天在些项目的时候碰到常用的GridView要实现一些分割线,之前就是用本方法利用listView和Item的背景颜色的不同线显示分割线.这是最low的一种做法.于是我就简单的写了一个自定义的 Gr ...