题意简介:
图片版
看到群里有人在讨论,觉得很有趣就跟同学讨论了一下。
然而讨论完没有得出什么建设性的结论,最终发现发题目的人也没有题解,只好作罢。
顺带一提,背景的第一段话非常毒瘤,因为后文读入中完全没有ai,aj这种东西,所以在我看来这一段是完全没有意义的。
但是zzy大师却通过某种方式从这段话中得出了某个结论
题意大概是给定一个 n<=1000 的无向连通图,要求从中选出一个点集S,使不存在某一条边使边的两端都在S点集中,且S点集外的每一个点都与S点集中的至少一个点直接相连。
求选取的方案数。
思路:
如果群里没人提到补图,我是不可能想到补图上去的,毕竟我从来没看过这样的题目(雾
对于n的点的无向连通图,补图与原图的并是一个完全图。
原图中没有连的边,补图中有连上;原图中连上的边,补图上没有。
要求选出的点集S内部点没有直接连边,则S点集在补图内可以形成一个完全图。
因为补图中的完全图意味着原图里没有边。
而对于第二个要求:
假如存在某个点在S外,且它没有与S点集中的点直接相连,这就意味着这个点在补图中,与S点集中的点都有连边。
换句话说,S可以再扩展,这个点可以被并入S,而并入后S的补图仍然是一个完全图。
因此,我们将问题转化为:求补图中不能再扩展的完全子图的个数。
于是就有了下面的暴力算法:
#include <cstdio>
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
#define Rep(i,a,b) for(REG int i=a;i>=b;i--)
inline char getc(){
static char buf[1<<14],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++;
}
inline int scan(){
REG int x=0; REG char ch=0;
while(ch<48) ch=getc();
while(ch>=48) x=x*10+ch-48,ch=getc();
return x;
}
const int N=1005;
bool match[N][N]; int n,m,d[N],cnt;
inline void dfs(int u,int k){
for(int i=1;i<=n;i++) if(!match[u][i]) d[i]++;
REG bool extra=0;
for(int i=1;i<=n;i++){
if(!match[u][i]&&d[i]==k){
extra=1;
if(i>u) dfs(i,k+1);
}
}
if(!extra) cnt++;
for(int i=1;i<=n;i++) if(!match[u][i]) d[i]--;
}
int main(){
n=scan(); m=scan();
rep(i,1,m){
int u=scan(),v=scan();
match[u][v]=match[v][u]=1;
}
rep(i,1,n) match[i][i]=1;
rep(i,1,n) dfs(i,1);
printf("%d",cnt);
return 0;
}
在上面的代码中,为了避免算重,我人为地加上了顺序的限制。
其实我还想了个n^3的dp,后来发现是假的,然后我就再也不会了。