100548I International Collegiate Routing Contest (01字典樹)
阿新 • • 發佈:2018-12-21
題意:給你多個子網掩碼,要你求一個最小的子網掩碼補集。
解題思路:IPV4一共有 2^32個地址,實際上對應的就是一個完全字典樹。現在給定的子網掩碼,實際上就是給定了一個字典樹,然後要你求這個字典樹的補樹,使得是一個完全字典樹。實際上就是求這個字典樹的所有補樹形成的森林。
那麼我們只要深搜即可。然後記錄下答案,然後還原下答案即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned int uint; const int MAXN=200050; int nxt[MAXN][2]; int sta[MAXN]; int tot=1; int root=1; int mask; void insert(uint x){ int cur=root; for(int i=31;i>=31-mask+1;i--){ uint c=((x>>i)&1); if(nxt[cur][c]==0) nxt[cur][c]=++tot; cur=nxt[cur][c]; } sta[cur]=1; } vector<pair<uint,int> > ans; void dfs(int u,int dep,uint sum){ if(u==0)//證明沒有覆蓋到 { ans.push_back({sum,dep}); return; } if(sta[u]==1)//後面就不用深搜了 { return; } dfs(nxt[u][0],dep+1,sum<<1); dfs(nxt[u][1],dep+1,(sum<<1)+1); } int main(){ int T; scanf("%d",&T); for(int qqq=1;qqq<=T;qqq++){ tot=1; root=1; memset(nxt,0,sizeof(nxt)); memset(sta,0,sizeof(sta)); int N; scanf("%d",&N); if(N==0)//特判 { printf("Case #%d:\n",qqq); printf("%d\n",1); printf("%d.%d.%d.%d/%d\n",0,0,0,0,0); continue; } int a,b,c,d; ans.clear(); for(int i=0;i<N;i++) { scanf("%d.%d.%d.%d/%d",&a,&b,&c,&d,&mask); uint sum=a;//加密 sum<<=8; sum+=b; sum<<=8; sum+=c; sum<<=8; sum+=d; insert(sum); } dfs(root,0,0); printf("Case #%d:\n",qqq); printf("%d\n",ans.size()); for(int i=0;i<ans.size();i++){ uint s=ans[i].first; int dd=ans[i].second; s<<=(32-dd);//解密 a=(s>>24); b=(s>>16)%(1<<8); c=(s>>8)%(1<<8); d=s%(1<<8); printf("%d.%d.%d.%d/%d\n",a,b,c,d,dd); } } return 0; }