1 条题解

  • 0
    @ 2026-9-23 23:39:00

    前言:

    1. 菜鸡 HydroKomide 在 CSP-S 与 WC 双双打铁,于是一怒之下开始爆肝非传统题目。

    2. 目前,这是一篇非 AC 题解,目前分数为 93pts(毕竟这种玄学题目需要经过大量调参才能 A)。

    3. 感谢机房大佬 xiaozengX 、王君诺、青莲武者 和 蒟蒻Ivan 等人的思路与精神上的支持。

    4. 本篇题解未完待续。

    思路:

    第一篇题解的想法很神,切成长度为 33 的小块在能够保证正确率的情况下使空间消耗尽量小。

    但是存储数字的那一步略显多余。我们考虑:怎样在不存具体数字的情况下最大化正确率?

    此处省略机房大辩论 114514114514 字。

    我们发现,扫描到一个三段字符时,只有可能发生以下两种情况:

    1. 使某一篇文章权值提高。
    2. 使某一或两篇文章不可能为答案(即完全排除可能性)。

    而这两种信息可以直接用两个字符维护。

    举例:一段信息为 abc24。

    其中,2 代表给 22 号文章加权值。4 二进制为 100,即完全排除 11 号文章。

    这样,当我们扫描到 abc 这个子串,直接加权值、排除文章即可。

    说这么多,具体实现呢?

    文本分析使用了多个程序逐步推进,相对比较复杂但思路顺畅。

    首先是拆解原文。把文章中每类的三连字符都计算个数。当然,由于文章长度不同,字符的相对权值肯定要乘上一个系数。

    这里使用了 map:

    #include<cstdio>
    #include<fstream>
    #include<map>
    #include<iostream>
    using namespace std;
    map<string,int>mp;
    char a,b,c;
    int main(){
    	freopen("文章名.txt","r",stdin);
    	freopen("ANALYZE_文章名.txt","w",stdout);
    	string str="   ";
    	scanf("%c%c%c",&a,&b,&c);
    	do{
    		str[0]=a,str[1]=b,str[2]=c;
    		mp[str]++;//每扫描一个字符存一次
    		a=b,b=c;
    	}while(scanf("%c",&c)!=EOF);
    	for(char i='!';i<='z';i++)
    		for(char j='!';j<='z';j++)
    			for(char k='!';k<='z';k++){//枚举所有可能性,这里省略了空格
    				string sss="   ";
    				sss[0]=i,sss[1]=j;sss[2]=k;
    				if(mp[sss]!=0)cout<<sss<<" "<<mp[sss]*文章系数<<"\n";
    			}
    	return 0;
    }
    

    每篇文章都用这个程序跑一遍,得到三个包含字符 + 相对权值的文本文档。

    然后就是生成数据库了。把三个分析结果的文本文档都读一遍,然后用上文所提的思路存储下来。

    为了更高的准确率,我们需要设定一个阈值系数,即容错率。确保一篇文章的相对权值显著高于另两篇文章。这里设为 1.61.6。

    #include<fstream>
    #include<map>
    #include<cstring>
    #include<iostream>
    #define ri register int
    using namespace std;
    const double COR=1.6;//阈值系数
    map<string,int>Smp,Mmp,Pmp;
    string str;
    int cnt;
    int main(){
    	freopen("PROGRAM.txt","w",stdout);
    	ifstream Sin("ANALYZE_S.txt");
    	ifstream Min("ANALYZE_M.txt");
    	ifstream Pin("ANALYZE_P.txt");//上个程序生成的文档
    	while(Sin>>str){
    		Sin>>cnt;
    		Smp[str]=cnt;
    	}
    	while(Min>>str){
    		Min>>cnt;
    		Mmp[str]=cnt;
    	}
    	while(Pin>>str){
    		Pin>>cnt;
    		Pmp[str]=cnt;//全部读入map
    	}
    	for(char i='!';i<='z';i++)
    		for(char j='!';j<='z';j++)
    			for(char k='!';k<='z';k++){
    				string sss="   ";
    				sss[0]=i,sss[1]=j,sss[2]=k;
    				int del=0,pls=0;
    				double S=0,M=0,P=0;
    				if(Smp.count(sss)!=0)S=Smp[sss];
    				if(Mmp.count(sss)!=0)M=Mmp[sss];
    				if(Pmp.count(sss)!=0)P=Pmp[sss];
    				if(S==0&&M==0&&P==0)continue;
    				if(S==0)del|=(1<<2);
    				if(M==0)del|=(1<<1);
    				if(P==0)del|=(1<<0);//如果文章权值为0,即可完全排除这篇文章
    				if(S>M*COR&&S>P*COR)pls=1;
    				if(M>S*COR&&M>P*COR)pls=2;
    				if(P>S*COR&&P>M*COR)pls=3;//确保阈值
    				if(pls==0&&del==0)continue;
    				printf("%c%c%c%d%d",i,j,k,pls,del);
    			}
    	return 0;
    }
    

    当然,由于我们带上了标点符号,所以还要在生成的所有双引号前加上 \。最后结果是这样一个东西。

    所以能够提交的程序也就来了:link

    后记:

    玄学题目是最好骗分、最具思维性,但最不容易 AC 的一类题目,同时也最贴近日常应用的。所以为什么 OI 反而不考这一类呢?

    当然,还没用高进制压缩就有 35KB 的长度,这也给后续的工作留下了空间。经对拍,在 ss 长度为 100100 左右时本代码正确率显著劣于题解的 AC 代码。下一步可以考虑存一些长段的语句辅助判断,或者对于长句子使用哈希。

    有错误欢迎评论指正,其他好思路欢迎与 HydroKomide 私信讨论(

    THE END

    • 1

    信息

    ID
    2369
    时间
    5000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者