首页 > 其他分享 >CF292C Beautiful IP Addresses 题解(两种写法)

CF292C Beautiful IP Addresses 题解(两种写法)

时间:2024-07-06 19:55:51浏览次数:23  
标签:Beautiful res Addresses cnta int 题解 num val ans

题意

一个 IP 地址是一个 32 位的 2 进制整数,分成四组 8 位的 2 进制整数(没有前导 0)。

比如说,0.255.1.123 是一个正确的 IP 地址,而0.256.1.123 和 0.255.1.01 不是正确的。

定义一个合法的回文 IP 地址为 Beautiful IP Address (回文地址就是去掉“.”后是个回文字符串的地址), 给出 n 个数字,求出所有可用这些数字组成的Beautiful Addresses(数字可以重复使用,但不能有一个数字不被使用)。

思路1

先枚举一个用所给的n个数字构成字符串(长度2-6),用它构造一个回文串。然后枚举切割点,即“.”放置的位置,并同时判断每一组数字是否是0-255之间的整数并且没有前导0。将每一个合法的答案记录下来,最后输出。

#include<bits/stdc++.h>
using namespace std;
int n,x[15],rec[5],cnta,ans[1000000][5];
int val(string s,int a,int b){//将字符串s中[a,b)这一段的值算出来
    int res=0;
    bool flag=0;
    for(int i=a;i<b;++i){
        if(s[i]!='0') flag=1;
        if(s[i]=='0'&&flag==0&&i!=b-1) return 256;//前导0不合法
        res=res*10+s[i]-'0';
    }
    return res;
}
void cutIP(string s){//枚举三个'.'放在哪里
    int si=s.size();
    for(int i=1;i<si-2;i++){
        if(val(s,0,i)>255) continue;
        for(int j=i+1;j<si-1;j++){
            if(val(s,i,j)>255) continue;
            for(int k=j+1;k<si;k++){
                if(val(s,j,k)>255||val(s,k,si)>255) continue;
                cnta++;
                ans[cnta][0]=val(s,0,i);
                ans[cnta][1]=val(s,i,j);
                ans[cnta][2]=val(s,j,k);
                ans[cnta][3]=val(s,k,si);
            }
        }
    }
}
void buildIP(int cnt,string s){//将一个字符串对称,成为一个回文串
	string res=s;
	reverse(s.begin(),s.end());
	if(cnt%2) s.erase(0,1);
	res=res+s;
	cutIP(res);
}
bool checku(string s){//检查是否有数字没有用到
	bool flag[15]={0};
	int si=s.size(),cnt=0;
	for(int i=0;i<si;i++)
		if(!flag[s[i]-'0']){
			flag[s[i]-'0']=1;
			cnt++;
		}
	if(cnt<n) return 0;
	else return 1;
}
void IP(int cnt,int ps,string s){
	if(ps>(cnt+1)/2){
		if(checku(s))
			buildIP(cnt,s);	
		return;
	}
	for(int i=1;i<=n;i++) IP(cnt,ps+1,s+char(x[i]+'0'));
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&x[i]);
	for(int i=4;i<=12;i++)//枚举IP地址的长度
		IP(i,1,"");
	printf("%d\n",cnta);
	for(int i=1;i<=cnta;i++){
		printf("%d",ans[i][0]);
		for(int j=1;j<4;j++) printf(".%d",ans[i][j]);
		printf("\n");
	}
	return 0;
} 

思路2

预处理出0-255中所有数的位数(cnum),每一位(num),包含的数字(h,通过位运算,类似于状压dp,将bool数组转成一个整数),能不能用(flag,保证用到的数里没有未给出的数字)。应当包含哪些数(即给出的数,have,与h原理相同),ex[i] 是i是否被包含。

再枚举每一段的数(0-255),判断是否包含所有给出的数且不包含其他数,是否是回文串。

最后ans记录答案并输出。具体见代码。

主要细节:0的处理。由于t=i=0,无法进入while循环,要将预处理中的每个数组的第0个进行特殊判断处理。

代码优势:预处理后常数很小,代码结构清晰不易写错,好调试。

#include<bits/stdc++.h>
using namespace std;
int n,x[15],have,h[256],num[256][4],cnum[256],cntans;
bool flag[256],ex[10];
struct answer{
	int p,q,s,t;
} ans[1000005];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&x[i]);
		have|=(1<<x[i]);
		ex[x[i]]=1;
	}
	h[0]=1;
	num[0][++cnum[0]]=0;
	for(int i=0;i<256;i++){
		int t=i;
		flag[i]=1;
		while(t){
			h[i]|=(1<<(t%10));
			num[i][++cnum[i]]=t%10;
			if(!ex[t%10]) flag[i]=0;
			t/=10;
		}
		if(i==0&&!ex[0]) flag[i]=0;
	}
	for(int a=0;a<256;a++){
		if(!flag[a]) continue;
		for(int b=0;b<256;b++){
			if(!flag[b]) continue;
			for(int c=0;c<256;c++){
				if(!flag[c]) continue;
				if((h[a]|h[b]|h[c])!=have) continue;
                //这个剪枝很关键,前三个数确定,IP地址已经过半,可以判断是否包含了所有数
				for(int d=0;d<256;d++){
					if(!flag[d]) continue;
					int res[20],cntr=0;
					for(int i=cnum[a];i>=1;i--) res[++cntr]=num[a][i];
                    //while x%10 处理出来的数位是倒着的,将它们合并时也得倒着
					for(int i=cnum[b];i>=1;i--) res[++cntr]=num[b][i];
					for(int i=cnum[c];i>=1;i--) res[++cntr]=num[c][i];
					for(int i=cnum[d];i>=1;i--) res[++cntr]=num[d][i];
					bool f=1;
					for(int i=1;i<cntr-i+1;i++)
						if(res[i]!=res[cntr-i+1]){
							f=0;
							break;
						}
					if(f) ans[++cntans].p=a,ans[cntans].q=b,ans[cntans].s=c,ans[cntans].t=d;
				}
			}	
		}
	}
	printf("%d\n",cntans);
	for(int i=1;i<=cntans;i++) printf("%d.%d.%d.%d\n",ans[i].p,ans[i].q,ans[i].s,ans[i].t);
	return 0;
} 

标签:Beautiful,res,Addresses,cnta,int,题解,num,val,ans
From: https://blog.csdn.net/2401_84512298/article/details/140183662

相关文章

  • 沪越联赛 - 2024年6月月赛Div2 题解
    问题A:替换题目描述小明每次注释的时候都写\(n\)个/。小红看了小明的程序,觉得太难看了,那么多/,所以决定把这些没用的/都去掉。小红定义了一个宏命令,每次可以将所有连续的\(m\)个/替换成空(注意不是空格)小明得知了小红的企图后非常着急,因为他知道光把/都去掉,那么原......
  • C++题解(3) 信息学奥赛一本通: 1013:温度表达转化 洛谷:B2013 温度表达转化 土豆编程:M
    【题目描述】利用公式 C=5×(F−32)÷9C=5×(F−32)÷9(其中CC表示摄氏温度,FF表示华氏温度)进行计算转化,输入华氏温度FF,输出摄氏温度CC,要求精确到小数点后55位。【输入】输入一行,包含一个实数FF,表示华氏温度。(F≥−459.67)(F≥−459.67)【输出】输出一行,包含一个......
  • [AGC064D] Red and Blue Chips 题解
    题目链接点击打开链接题目解法挺牛的题这种计数本质不同的结果的题,一个很不错的切入口是判断结果的合法性令B的总数为\(m\)我们把结果串先挂在第\(m\)个B上考虑从后往前枚举原串(最后一个B不枚举),相当于我们在倒序模拟操作过程枚举到B,我们相当于要把后面的一个B......
  • HT-014 Div3 扫雷 题解 [ 绿 ] [ 二维差分 ]
    分析观察到是曼哈顿距离\(\ler\)的范围可以扫到,联想到如下图形:左边是\(r=1\)可以扫到的范围,右边是\(r=2\)可以扫到的范围。于是,我们只要对这样的图形在\(1000*1000\)的格子里差分一下就好了。但这样的复杂度是\(O(nm)\)的,会死的很惨。优化不难发现这个图形是一......
  • HT-014 Div3 跳棋 题解 [ 黄 ] [ 并查集 ] [ 树型结构 ]
    分析依旧是一个连通块题。观察题面不难发现两个重要性质:一个跳棋只能以它旁边的两个跳棋为中点跳跃,且满足跳跃路线中除中点以外没有其它跳棋阻挡。只有我们的跳棋可以移动。跳棋的操作具有可逆性/对称性。第三条性质可以这么理解,就是一个跳棋跳过去之后,它还可以跳回来。......
  • [POI2015] WYC 题解
    [POI2015]WYC显然矩阵乘法发现点数和边权非常小,所以可以考虑拆点把每个点拆为\(u_1\),\(u_2\),\(u_3\),初始:\(u_1\tou_2\),\(u_2\tou_3\),每一条加边:\(u+(w-1)\timesn\tov\)因为\(k\)非常大,所以考虑倍增优化注意:答案会爆longlong,需要使用unsignedlonglong//Pro......
  • python-docx库 写入docx时中文不适配问题,中文异常问题解决办法。
    python-docx库写入docx时中文不适配问题,中文异常问题解决办法。通过以下方法可以成功将正文修改为宋体字体。这个是全文设置。fromdocx.oxml.nsimportqndoc=Document()doc.styles['Normal'].font.name=u'宋体'doc.styles['Normal']._element.rPr.rFonts.set(qn('w:......
  • ZeroMQ最全面试题解读(3万字长文)
    目录解释ZeroMQ是什么,它的主要用途是什么?ZeroMQ支持哪些通信模式?描述一下ZeroMQ中的“消息”和“消息帧”如何在C++中初始化一个ZeroMQ上下文?在ZeroMQ中,如何创建一个套接字并将其绑定到特定端口?解释什么是“管道模式”(PipePattern)说明如何使用ZeroMQ进行点对点通信Zer......
  • 题解:CF1256D Binary String Minimizing
    贪心。数据范围\(n\le10^{6}\),因此我们要用时间复杂度为\(\mathcal{O}\left(n\right)\)的算法来解决这个问题。思路从左至右扫一遍序列,如果遇到\(10\),则要将这个\(0\)交换到前面的位置。由于是字典序最小,\(0\)应该尽量在最高位。现在需要知道这个\(0\)被交换到哪......
  • 2016 CSP-J/NOIP万字长文复赛真题题解——秒杀T1 买铅笔,T2 回文日期,T3 海港,T4 魔法
    [NOIP2016普及组]买铅笔题干[NOIP2016普及组]买铅笔题目背景NOIP2016普及组T1题目描述P老师需要去商店买nnn支铅笔作为小朋友们参加NOIP的礼物。她发现......