题目
1.完美对 有n个物品,每个物品有k个属性,第i件物品的第j个属性用一个正整数表示记为,两个不同的物品讠,j被称为是完美对的当目仅当ai1+aj1=ai2+aj2=...=aik+ajk,求完美对的个数。 第一行两个数字n,k。 接下来n行,每行k个数字表示。
baoli
输入例子:
5 3
2 11 21
19 10 1
20 11 1
6 15 24
18 27 36
输出例子:
3
暴力破解
我第一反应就暴力破解,做好了超时的准备
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
int count=0;
if(a==1)
System.out.println(0);
else{
int[][] c=new int[a][b];
for(int i=0;i<a;i++){
for(int j=0;j<b;j++){
c[i][j]=in.nextInt();
}
}
for(int i=0;i<a;i++){
for(int j=i+1;j<a;j++){
int sum=c[i][0]+c[j][0];
for(int k=0;k<b;k++){
if(sum!=c[i][k]+c[j][k]){
break;
}
if(k==b-1){
count++;
}
}
}
}
System.out.println(count);
}
}
}
}
结果不出我所料,超时了,因为时间复杂度已经达到了O(n^3)
级别了。
优化思路
其实暴力破解的时候就想到了做减法,因为把等式两边移项就是同一列了。
原式 | 等值变换 |
ai,1+aj,1=ai,2+aj,2 | ai,1-ai,2=aj,2-aj,1 |
ai,2+aj,2=ai,3+aj,3 | ai,2-ai,3=aj,3-aj,2 |
ai,3+aj,3=ai,4+aj,4 | ai,3-ai,4=aj,4-aj,3 |
... | ... |
ai,k-1+aj,k-1=ai,k+aj,k | ai,k-1-ai,k=aj,k-aj,k-1 |
这样子相邻数做差,如果是完美对,他的数值绝对值是一样的。我们取正数然后拼接字符串,如果字符串一样且相邻数做差所得数组的和相加为0,就是完美对。
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
int count=0;
int[][] c=new int[a][b];
String[]code=new String[a];
for(int i=0;i<a;i++){
c[i][0]=in.nextInt();
StringBuilder s=new StringBuilder();
for(int j=1;j<b;j++){
c[i][j]=in.nextInt();
int aa=c[i][j]-c[i][j-1];
if(aa>0)
s.append("+"+aa);
else
s.append(aa);
}
code[i]=s.toString();
}
for(int i=0;i<a;i++){
String a2 =code[i].replaceAll("\\+","#");
a2=a2.replaceAll("-", "@");
for(int j=i+1;j<a;j++){
String a1 =code[j].replaceAll("-","#");
a1 =a1.replaceAll("\\+","@");
if(a2.equals(a1)){
count++;
}
}
}
System.out.println(count);
}
}
}
因为+变成-,再把-变成+的话,就全部变成+了(第一步的+又变回了+),所以用其他符号代替了。结果还是超时了,我用了哈希表优化,并且自己写了replaceAll方法。
import java.util.Scanner;
import java.util.HashMap;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static String change(String str) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == '+') {
result.append('-');
} else if (c == '-') {
result.append('+');
} else {
result.append(c);
}
}
return result.toString();
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
HashMap<String, Integer> codeCount = new HashMap<>();
int count=0;
int[][] c=new int[a][b];
String[]code=new String[a];
for(int i=0;i<a;i++){
c[i][0]=in.nextInt();
StringBuilder s=new StringBuilder();
for(int j=1;j<b;j++){
c[i][j]=in.nextInt();
int aa=c[i][j]-c[i][j-1];
if(aa>0)
s.append("+"+aa);
else
s.append(aa);
}
code[i]=s.toString();
codeCount.compute(code[i], (key, value) -> (value == null) ? 1 : value + 1);
}
for(int i=0;i<a;i++){
String j=change(code[i]);
if(codeCount.containsKey(j)){
count+=codeCount.get(code[i])*codeCount.get(j);
codeCount.remove(j);
codeCount.remove(code[i]);
}
}
System.out.println(count);
}
}
}
第一组数据就报错了,不知道为什么,凌晨1点了直接倒头睡觉了,梦里都在想怎么回事。
今天起床用自测数据试了一下,发现是等值数组会出现问题,因为其他都是两个不同的codeCount,但是如果测试用例下面这种情况:
5 3
2 2 2
1 1 1
3 3 3
4 4 4
1 1 1
那么理论输出是10,结果输出的是5*5/2=12了,因此要考虑到这种特殊情况,全0的codeCount是同一个,它的计算方法是C的n取2,因此改进代码如下:
AC代码
import java.util.Scanner;
import java.util.HashMap;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
HashMap<String, Integer> codeCount = new HashMap<>();
int count=0;
int[][] c=new int[a][b];
String[]code=new String[a];
for(int i=0;i<a;i++){
c[i][0]=in.nextInt();
StringBuilder s=new StringBuilder();
StringBuilder sf=new StringBuilder();
for(int j=1;j<b;j++){
c[i][j]=in.nextInt();
int aa=c[i][j]-c[i][j-1];
s.append(aa);
sf.append(-aa);
}
code[i]=s.toString();
codeCount.compute(code[i], (key, value) -> (value == null) ? 1 : value + 1);
code[i]=sf.toString();
}
for(int i=0;i<a;i++){
String j=code[i];
if(codeCount.containsKey(j)){
if(j.replaceAll("0", "").length() == 0){
count+=codeCount.get(j)*(codeCount.get(j)-1);
codeCount.remove(j);
}
else
count+=codeCount.get(j);
}
}
System.out.println(count/2);
}
}
}
终于也是通过了所有的案例点了,家人们,不容易啊