P1013 [NOIP1998 提高组] 进制位
阅读原文时间:2021年09月25日阅读:1

看到这道题时,有没有想到搜索?然后就是一通码……然后过了。

但是,真的要用搜索吗?

我们可以观察一下。对于n进制中的数ii,如果ii加上某一个数jj会变成两位数,那么可以得到如下不等式:

i+j>n−1⇒j>n−1−ii+j>n−1⇒j>n−1−i

而满足要求的jj的个数有n−1−(n−1−i)=in−1−(n−1−i)=i个。由此我们可以得到结论,一个字母的值就是这个字母对应的行中两位数的个数。我们所需要做的只是验证是否正确。那么怎样验证呢?最直接的办法是直接往里面代,但能否用另外的方法将每个字母的值算出呢?

这个比较难想。对于一个数ii,如果想要j+kj+k的个位数为ii,必须满足i<k<ni<k<n。那么,假设满足条件的kk有a[i]a[i]个,ii的值就是n−1−a[i]n−1−a[i]。a[i]a[i]只用求一个字母在两位数的个位上出现的次数即可。

另外,如果一个数在同一行中出现了两次,显然也是不对的,直接结束即可。

在下面的代码中,因为行数是nn,所以其实是n−1n−1进制的加法。

#include

#define fu(i,q,w) for(register int i=q;i<=w;i++) #define fd(i,q,w) for(register int i=q;i>=w;i--)
using namespace std;
typedef long long ll;
inline int read(){
int ret=0,f=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
while(c>='0'&&c<='9')ret=ret*10+(c-'0'),c=getchar(); return ret*f; } char word[10];//记录字母 char check[10];//检查重复 string numx,numy;//储存输入数据、检查重复 map two;//一行中两位数个数
map tone;//存字母在两位数个位出现几次
int n;
void in(){
n=read();
cin>>numx;//"+"特判输入
fu(i,1,n-1){cin>>numx,word[i]=numx[0];}// 第一行存表头的每个字母
fu(i,1,n-1)//从第二行开始
fu(j,1,n){cin>>numx;

if(j!=1&&j!=2)//表头不算
if(numx==numy){printf("ERROR!");exit(0);}//发现重复输入一定不对
numy=numx; //前后比,不要全行比
if(numx.size()==2){//统计两位数个数
two[word[i]]++;tone[numx[1]]++;
}
}
}
void solve(){
fu(i,1,n-1)
if(two[word[i]]!=n-2-tone[word[i]]){printf("ERROR!");exit(0);}
//比较两种算法的结果是否相同
fu(i,1,n-1)
cout<<word[i]<<'='<<two[word[i]]<<' ';
printf("\n");
printf("%d",n-1);
}
int main(){
in();
solve();
return 0;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章