CIDR合併(CCF 201812-3)

最終的100分代碼

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

class IP{
	int[] value;
	int mask;
	
	public IP() {
		this.value = new int[4];
		for(int i=0;i<4;i++) {
			this.value[i]=0;
		}
	}

	@Override
	public String toString() {
		return value[0]+"."+value[1]+"."+value[2]+"."+value[3]+"/"+mask;
	}

}

public class Main {

	public static void main(String[] args) throws IOException{
		
		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		
		int n = Integer.parseInt(bf.readLine());
		String rawIp;
		// 轉換爲標準形式
		ArrayList<IP> ipArr = new ArrayList<IP>();
		for(int i=0;i<n;i++) {
			rawIp = bf.readLine();
			ipArr.add(toIp(rawIp));
		}
		
		// 排序
		Collections.sort(ipArr, new Comparator<IP>() {
			@Override
			public int compare(IP o1,IP o2) {
				int flag = greaterThan(o1,o2);
				if(flag==0) {
					// 前綴長度
					if(o1.mask>o2.mask) {
						flag = 1;
					}else {
						flag = -1;
					}
				}
				return flag;
			}
		});
		
		IP[] newArr = new IP[ipArr.size()];            // 轉爲數組
		boolean[] delete = new boolean[ipArr.size()];  // 是否被刪除
		ipArr.toArray(newArr);
		
		int relation;
		int right = 0;
		int len = newArr.length;
		boolean flag = false;
		for(int i=0;i<len-1;) {
			// 刪除跳過
			if(delete[i]) {
				i++;
				continue;
			}

			// 找到右邊一個
			do {
				right++;
				if(right>=len) {
					flag = true;
					break;
				}
			}while(delete[right] || right==i);
			if(flag) {
				break;
			}

			relation = relation(newArr[i],newArr[right]);

			if(relation==1) {
				// 包含
//				System.out.println("吞 "+newArr[i]+" <- "+newArr[right]+" 刪"+right);
				delete[right] = true;
			}else if(relation==2) {
				// 合併
//				System.out.println("合 "+newArr[i]+" & "+newArr[right]+" 刪"+right);
				newArr[i].mask -= 1;
				delete[right] = true;
				if(i>0) {
					right=i-1;
					i--;
					while(delete[i]) {
						i--;
					}
				}
			}else {
				i++;
			}
		}

		for(int i=0;i<newArr.length;i++) {
			if(!delete[i]) {
				System.out.println(newArr[i]);
			}
		}
	}
	
	// 包含返回1,合併返回2
	public static int relation(IP a,IP b) {

		if(a.mask<=b.mask) {
			int part = (a.mask-1)/8;
			int digit = a.mask - part*8;
			
			for(int i=0;i<part;i++) {
				if(a.value[i]!=b.value[i]) {
					return 0;
				}
			}
			
			int ap = a.value[part] >> (8-digit);
			int bp = b.value[part] >> (8-digit);

			if(ap==bp) {
				return 1;
			}
			
			if(a.mask==b.mask && bp-ap==1 && ((ap>>1)==(bp>>1))) {
				return 2;
			}

		}
		
		return 0;
	}
	
	// 比較兩個IP
	public static int greaterThan(IP o1,IP o2) {	
		int flag = 1;
		
		int a = (o1.value[0]<<8) + o1.value[1] - (o2.value[0]<<8) - o2.value[1];
		
		if(a>0) {
		}else if(a<0) {
			flag = -1;
		}else {
			int b = (o1.value[2]<<8) + o1.value[3] - (o2.value[2]<<8) - o2.value[3];
			flag = b;
		}
		
		return flag;
	}
	
	// 處理輸入的ip
	public static IP toIp(String rawIp) {
		int split = rawIp.indexOf('/');
		String value = rawIp;
		int mask = 0;
		// 含有'/',分割
		if(split>0) {
			value = rawIp.substring(0, split);
			mask = Integer.parseInt(rawIp.substring(split+1, rawIp.length()));
		}

		IP ip = new IP();
		ip.mask = mask;
		
		// 處理地址
		for(int i=0;i<4;i++) {
			if(value.contains(".")) {
				// 繼續分割
				split = value.indexOf('.');
				ip.value[i] = Integer.parseInt(value.substring(0,split));
				value = value.substring(split+1,value.length());
			}else if(value.length()>0) {
				// 只有數字
				ip.value[i] = Integer.parseInt(value);
				value = "";
				// 若無前綴長度
				if(ip.mask==0) {
					ip.mask = (i+1)*8;
				}
			}else {
				// 空
				ip.value[i] = 0;
			}
		}
		
		return ip;
	}
	
}

踩的兩個最大的坑:

  1. 同級合併的細節
  2. 超時

悲慘的改代碼過程:

第一步,處理不同格式ip輸入
測試輸入

7
101.6.6.0/24
101.6.6/23
101/8
1/32
101.6.6.0
101.6
1

標準形式

101.6.6.0/24
101.6.6.0/23
101.0.0.0/8
1.0.0.0/32
101.6.6.0/32
101.6.0.0/16
1.0.0.0/8

第二步,排序

1.0.0.0/8
1.0.0.0/32
101.0.0.0/8
101.6.0.0/16
101.6.6.0/23
101.6.6.0/24
101.6.6.0/32

此時提交可通過測試點1-4,有60分

下一步,從小到大合併,80分

最後一步,同級合併

測試1

輸入

4
206.0.68.0/25
206.0.68.128/25
206.0.69.0/25
206.0.69.128/25

輸出

206.0.68.0/23

測試2

輸入

12
206.0.68.0/25
206.0.68.128/25
206.0.69.0/25
206.0.69.128/25
206.0.70.0/26
206.0.70.64/26
206.0.70.128/26
206.0.70.192/26
206.0.71.128/26
206.0.71.64/26
206.0.71.192/26
206.0.71.0/26

輸出

206.0.68.0/22

測試3

輸入

3
206.0.71.64/25
206.0.71.192/26
206.0.71.0/26

這種也可以合併,不過似乎不用考慮?

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;

class IP{
	int[] value;
	int mask;
	
	public IP() {
		this.value = new int[4];
		for(int i=0;i<4;i++) {
			this.value[i]=0;
		}
	}

	@Override
	public String toString() {
		return value[0]+"."+value[1]+"."+value[2]+"."+value[3]+"/"+mask;
	}

}

public class Main {

	public static void main(String[] args) {
		
		Scanner s = new Scanner(System.in);
		
		int n = Integer.parseInt(s.nextLine());
		String rawIp;
		
		// 轉換爲標準形式
		ArrayList<IP> ipArr = new ArrayList<IP>();
		for(int i=0;i<n;i++) {
			rawIp = s.nextLine();
			ipArr.add(toIp(rawIp));
		}
		
		// 排序
		Collections.sort(ipArr, new Comparator<IP>() {
			@Override
			public int compare(IP o1,IP o2) {
				int flag = greaterThan(o1,o2);
				if(flag==0) {
					// 前綴長度
					if(o1.mask>o2.mask) {
						flag = 1;
					}else {
						flag = -1;
					}
				}
				return flag;
			}
		});
		
		// 從小到大合併
		for(int i=0;i<ipArr.size()-1;i++) {
			if(contain(ipArr.get(i),ipArr.get(i+1))) {
				ipArr.remove(i+1);
			}
		}
		
		// 同級合併
		for(int i=0;i<ipArr.size()-1;i++) {
			if(canMerge(ipArr.get(i),ipArr.get(i+1))) {
				ipArr.get(i).mask -= 1;
				ipArr.remove(i+1);
				if(i>0) {
					i -= 2;
				}
			}
		}
		
		for(int i=0;i<ipArr.size();i++) {
			System.out.println(ipArr.get(i));
		}
	}
	
	// 檢查是否可以合併
	public static boolean canMerge(IP a,IP b) {
		boolean flag = false;
		
		if(a.mask==b.mask) {
			int part = (a.mask-1)/8;
			int digit = a.mask - part*8;
			
			IP newA = new IP();
			for(int i=0;i<4;i++) {
				newA.value[i] = a.value[i];
			}
			newA.value[part] += 1<<(8-digit);
			
			if(greaterThan(newA,b)==0) {
				flag=true;
			}
		}
		
		return flag;
	}
	
	// 檢查a是否包含b
	public static boolean contain(IP a,IP b) {
		boolean flag = false;
		
		if(a.mask<=b.mask) {
			IP aMax = getMaxIp(a);
			IP bMax = getMaxIp(b);
			
			if(greaterThan(b,a)>=0 && greaterThan(aMax,bMax)>=0) {
				flag = true;
			}
		}
		
		return flag;
	}
	
	// 比較兩個IP
	public static int greaterThan(IP o1,IP o2) {
		int flag = 1;
		int a = (o1.value[0]*256 + o1.value[1]) - (o2.value[0]*256 + o2.value[1]);
		if(a>0) {
		}else if(a<0) {
			flag = -1;
		}else {
			int b = (o1.value[2]*256 + o1.value[3]) - (o2.value[2]*256 + o2.value[3]);
			if(b<0){
				flag = -1;
			}else if(b==0) {
				flag = 0;
			}
		}
		return flag;
	}
	
	// 根據mask獲取最大IP
	public static IP getMaxIp(IP ip) {
		IP maxIP = new IP();
		
		int ms = 32-ip.mask;
		int[] m = new int[4];
		for(int i=3;i>=0;i--) {
			if(ms>=8) {
				m[i]=8;
				ms-=8;
			}else {
				m[i]=ms;
				break;
			}
		}
		
		for(int i=0;i<4;i++) {
			maxIP.value[i] = ip.value[i] + (1<<m[i]) - 1;
		}
		
		return maxIP;
	}
	
	// 處理輸入的ip
	public static IP toIp(String rawIp) {
		int split = rawIp.indexOf('/');
		String value = rawIp;
		int mask = 0;
		// 含有'/',分割
		if(split>0) {
			value = rawIp.substring(0, split);
			mask = Integer.parseInt(rawIp.substring(split+1, rawIp.length()));
		}

		IP ip = new IP();
		ip.mask = mask;
		
		// 處理地址
		for(int i=0;i<4;i++) {
			if(value.contains(".")) {
				// 繼續分割
				split = value.indexOf('.');
				ip.value[i] = Integer.parseInt(value.substring(0,split));
				value = value.substring(split+1,value.length());
			}else if(value.length()>0) {
				// 只有數字
				ip.value[i] = Integer.parseInt(value);
				value = "";
				// 若無前綴長度
				if(ip.mask==0) {
					ip.mask = (i+1)*8;
				}
			}else {
				// 空
				ip.value[i] = 0;
			}
		}
		
		return ip;
	}
	
}

結果:得分80,時間使用1.39s,空間使用171.7MB

把Scanner改成BufferedReader
結果:得分80,時間使用1.234s,空間使用103.4MB

難道是超時
心好痛

修改從小到大合併的方法

一些測試數據

10.0/9
10.28/18

10.0/9
10.1/9

10.0/1
10.0/32

101.6.6.0/24
101.6.7.0/24

101.6.6.0/24
101.6.6.128/24

結果:得分80,時間使用890ms,空間使用95.19MB

快了一些,但是還是80分,說明前面不是超時的問題,還是同級合併有問題

改啊改啊改

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

class IP{
	int[] value;
	int mask;
	
	public IP() {
		this.value = new int[4];
		for(int i=0;i<4;i++) {
			this.value[i]=0;
		}
	}

	@Override
	public String toString() {
		return value[0]+"."+value[1]+"."+value[2]+"."+value[3]+"/"+mask;
	}

}

public class Main {

	public static void main(String[] args) throws IOException{
		
		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		
		int n = Integer.parseInt(bf.readLine());
		String rawIp;
		
		// 轉換爲標準形式
		ArrayList<IP> ipArr = new ArrayList<IP>();
		for(int i=0;i<n;i++) {
			rawIp = bf.readLine();
			ipArr.add(toIp(rawIp));
		}
		
		// 排序
		Collections.sort(ipArr, new Comparator<IP>() {
			@Override
			public int compare(IP o1,IP o2) {
				int flag = greaterThan(o1,o2);
				if(flag==0) {
					// 前綴長度
					if(o1.mask>o2.mask) {
						flag = 1;
					}else {
						flag = -1;
					}
				}
				return flag;
			}
		});
		
		int relation;
		for(int i=0;i<ipArr.size()-1;) {
			relation = relation(ipArr.get(i),ipArr.get(i+1));
			if(relation==1) {
//				System.out.println("吞 "+ipArr.get(i)+" <- "+ipArr.get(i+1));
				ipArr.remove(i+1);
			}else if(relation==2) {
//				System.out.println("合 "+ipArr.get(i)+" & "+ipArr.get(i+1));
				ipArr.get(i).mask -= 1;
				ipArr.remove(i+1);
				if(i>0) {
					i--;
				}
			}else {
				i++;
			}
		}

		for(int i=0;i<ipArr.size();i++) {
			System.out.println(ipArr.get(i));
		}
	}
	
	// 包含返回1,合併返回2
	public static int relation(IP a,IP b) {
		
		if(a.mask<=b.mask) {
			int part = (a.mask-1)/8;
			int digit = a.mask - part*8;
			
			for(int i=0;i<part;i++) {
				if(a.value[i]!=b.value[i]) {
					return 0;
				}
			}
			
			int ap = a.value[part] >> (8-digit);
			int bp = b.value[part] >> (8-digit);
			
			if(ap==bp) {
				return 1;
			}
			
			if(a.mask==b.mask && bp-ap==1 && ((ap>>1)==(bp>>1))) {
				return 2;
			}

		}
		
		return 0;
	}
	
	// 比較兩個IP
	public static int greaterThan(IP o1,IP o2) {
		int flag = 1;
		int a = (o1.value[0]*256 + o1.value[1]) - (o2.value[0]*256 + o2.value[1]);
		if(a>0) {
		}else if(a<0) {
			flag = -1;
		}else {
			int b = (o1.value[2]*256 + o1.value[3]) - (o2.value[2]*256 + o2.value[3]);
			if(b<0){
				flag = -1;
			}else if(b==0) {
				flag = 0;
			}
		}
		return flag;
	}
	
	// 處理輸入的ip
	public static IP toIp(String rawIp) {
		int split = rawIp.indexOf('/');
		String value = rawIp;
		int mask = 0;
		// 含有'/',分割
		if(split>0) {
			value = rawIp.substring(0, split);
			mask = Integer.parseInt(rawIp.substring(split+1, rawIp.length()));
		}

		IP ip = new IP();
		ip.mask = mask;
		
		// 處理地址
		for(int i=0;i<4;i++) {
			if(value.contains(".")) {
				// 繼續分割
				split = value.indexOf('.');
				ip.value[i] = Integer.parseInt(value.substring(0,split));
				value = value.substring(split+1,value.length());
			}else if(value.length()>0) {
				// 只有數字
				ip.value[i] = Integer.parseInt(value);
				value = "";
				// 若無前綴長度
				if(ip.mask==0) {
					ip.mask = (i+1)*8;
				}
			}else {
				// 空
				ip.value[i] = 0;
			}
		}
		
		return ip;
	}
	
}

嗚嗚嗚90了
終於把同級合併改對了

看看還能不能快一點

把最後的遍歷從鏈表改成了數組,成功!

awsl

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章