CF1692F 3SUM - 题解
方法 1:暴力
用三层循环枚举每一种 \(i,j,k\) 的选择。
时间复杂度 \(O(T \times N^3)\),因为 $ 3 \leq n \leq 2 \cdot 10^5 $,所以明显不可取。
代码就不用了,因为我也没用暴力。
方法 2 根据 \(a\) 的个位来枚举
\(a_i,a_j,a_k\) 之和的个位,只取决于这三个数的个位。
即:如果这三个数的个位的和的个位为 \(3\),那么这三个数的和的个位必定为 \(3\)。
我们可以用 \(sum\) 记录每 \(a\) 数组内个个位数出现的次数,判断时把 \(sum_i,sum_j,sum_k\) 都减 \(1\),判断所有元素的出现次数(\(0 \sim 9\)),然后再判定能否在这些个位里面选三个,使得它们的和的个位为 \(3\),然后再加回去。
注意事项:
-
即便是个位相加,它们的和依然可能是两位数,所以还需要再 \(\bmod 10\) 一次。
-
不能只记录是否出现,例如 \(1\) 和 \(0\) 各出现一次,那么程序会判断 \(1,1,1\) 的组合是合法的,但这实际上不合法。
上代码:
#include<cstdio>
#include<cstring>
using namespace std;
int T;
int n;
int sum[15];
int read() //快读
{
int x=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*w;
}
int main()
{
scanf("%d",&T);
for(int I=1;I<=T;I++)
{
n=read();
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
int a=read(); //没有必要用数组来存
sum[a%10]++; //sum 记录每个个位出现的次数
}
bool ans=false;
for(int i=0;i<=9&&!ans;i++) //!ans 使得找到方案马上退出
for(int j=0;j<=9&&!ans;j++)
for(int k=1;k<=9&&!ans;k++)
{
sum[i]--,sum[j]--,sum[k]--;
if((i+j+k)%10==3&&(sum[i]>=0&&sum[j]>=0&&sum[k]>=0))
ans=true;
sum[i]++,sum[j]++,sum[k]++;
}
printf("%s\n",ans?"YES":"NO");
}
return 0;
}
方法 3:打表出奇迹
我们可以发现,某些固定的三个个位的组合是合法的,那么我们为什么不事先枚举出所有的组合,然后再看数据是否符合条件呢?
核心代码:
int s[100][5]={ //s 代表所有可能的组合情况
{0,0,3},{0,1,2},{0,2,1},{0,4,9},{0,5,8},{0,6,7},{0,7,6},{0,8,5},{0,9,4},
{1,0,2},{1,1,1},{1,3,9},{1,4,8},{1,5,7},{1,6,6},{1,7,5},{1,8,4},{1,9,3},
{2,0,1},{2,2,9},{2,3,8},{2,4,7},{2,5,6},{2,6,5},{2,7,4},{2,8,3},{2,9,2},
{3,1,9},{3,2,8},{3,3,7},{3,4,6},{3,5,5},{3,6,4},{3,7,3},{3,8,2},{3,9,1},
{4,0,9},{4,1,8},{4,2,7},{4,3,6},{4,4,5},{4,5,4},{4,6,3},{4,7,2},{4,8,1},
{5,0,8},{5,1,7},{5,2,6},{5,3,5},{5,4,4},{5,5,3},{5,6,2},{5,7,1},{5,9,9},
{6,0,7},{6,1,6},{6,2,5},{6,3,4},{6,4,3},{6,5,2},{6,6,1},{6,8,9},{6,9,8},
{7,0,6},{7,1,5},{7,2,4},{7,3,3},{7,4,2},{7,5,1},{7,7,9},{7,8,8},{7,9,7},
{8,0,5},{8,1,4},{8,2,3},{8,3,2},{8,4,1},{8,6,9},{8,7,8},{8,8,7},{8,9,6},
{9,0,4},{9,1,3},{9,2,2},{9,3,1},{9,5,9},{9,6,8},{9,7,7},{9,8,6},{9,9,5}
};
for(int i=0;i<90&&!ans;i++)
{
sum[s[i][0]]--,sum[s[i][1]]--,sum[s[i][2]]--;
if(sum[s[i][0]]>=0&&sum[s[i][1]]>=0&&sum[s[i][2]]>=0)
ans=true;
sum[s[i][0]]++,sum[s[i][1]]++,sum[s[i][2]]++;
}
附上打表程序:
for(int i=0;i<=9;i++)
{
for(int j=0;j<=9;j++)
for(int k=1;k<=9;k++)
if((i+j+k)%10==3) //如果满足条件则输出
printf("{%d,%d,%d},",i,j,k);
printf("\n");
}
2022-07-20 15:48 撰写于洛谷,2025-08-29 20:28 迁移至博客园。
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/19065126

浙公网安备 33010602011771号