• 神秘题目1


    题目

    问题描述

    给定一个 (n) 个点的竞赛图,求有多少个点的子集 (S) 满足 (S) 的诱导子图是强连通的。

    竞赛图是一个有向图,满足任意两个不同的点之间均存在且仅存在一条有向边相连。

    一个图在点集 (S) 的诱导子图的定义为:点集为 (S),边集为所有两端的点都在 (S) 中的边的子图。

    一个图是强连通的,当且仅当该图中任意两个点 (x),(y),都满足以 (x) 为起点存在一条路径到达 (y)

    注意空集也是一个子集,且我们定义空集的诱导子图也是强连通的。

    输入格式

    输入第一行有一个正整数 (T),表示测试点组数。

    对于每一个测试点,第一行输入一个数 (n),表示点的个数。

    接下来 n 行,每行 n 个数,设其为 (a_{i,j}),如果 (a_{i,j}) ,则表示从 (i)(j) 有一条有向边,否则表示没有。保证 (a_{i,i})(a_{i,j}hat{} a_{i,j}=1(i ot=j))

    输出格式

    输出有 (T) 行,对于每一个测试点输出其答案。

    样例输入1

    2
    3
    0 1 0
    0 0 1
    1 0 0
    5
    0 0 0 1 1
    1 0 1 0 1
    1 0 0 1 0
    0 1 0 0 1
    0 0 1 0 0
    

    样例输出1

    5
    14
    

    数据范围与约定

    对于 (20\%) 的数据,满足 (n≤10)

    对于 (40\%) 的数据,满足 (n≤15)

    对于 (60\%) 的数据,满足 (n≤19)

    对于 (100\%) 的数据,满足 (1≤n≤24,1≤T≤10)

    时间限制:2s

    空间限制:512MB

    题解

    20分暴力

    怎么做都行。

    40分暴力

    考虑状压,枚举 (n) 个点的状态,之后用 (Floyed) 判断即可。

    60分暴力

    考虑优化上一个做法,状压感觉是没法优化了,但是我们可以优化 (Floyed)

    不用对 (n) 个点都跑一边,只需要跑状态中出现过的点。

    其实也可以去掉 (Floyed) ,设 (f[i]) 表示 (n) 个点状态是 (i) 时的合法情况。

    对于每一个状态 (i) 考虑其去掉一个点 (x) 的合法状态 (i') (就是代码中的 (state))。

    如果 (i') 里面的点有指向 (x) 的出边,并且 (x) 有指向 (i') 中任意一点的出边,那么 (i) 是一个合法状态。

    注意特判三个点的情况。

    #include<bits/stdc++.h>
    using namespace std;
    int T,n,dis[30][30],ans,road[30],_road[30],Log[16778000],id[30];
    bool f[16778000];
    inline int read()
    {
    	int x=0,w=0;char ch=0;
    	while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return w?-x:x;
    }
    bool check(int state,int x)
    {
    	if(!state)return 1;
    	id[0]=0;
    	for(int i=state;i;i-=i&-i)
    		id[++id[0]]=Log[i&-i];
    	if(id[0]==1)return 0;
    	if(id[0]>2){
    		if(!f[state])return 0;
    		return (state&road[x])&&(state&_road[x]);
    	}
    	return (dis[id[1]][id[2]]&&dis[id[2]][x]&&dis[x][id[1]])||(dis[id[2]][id[1]]&&dis[x][id[2]]&&dis[id[1]][x]);
    }
    int main()
    {
    	T=read();
    	for(int i=1;i<=24;i++)Log[1<<(i-1)]=i;
    	while(T--){
    		n=read();
    		for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			dis[i][j]=read();
    		for(int i=1;i<=n;i++){
    			road[i]=_road[i]=0;
    			for(int j=1;j<=n;j++){
    				if(dis[i][j])road[i]+=1<<(j-1);
    				if(dis[j][i])_road[i]+=1<<(j-1);
    			}
    		}
    		for(int i=0,End=1<<n;i<End;i++)f[i]=0;
    		ans=f[0]=1;
    		for(int i=1,End=1<<n;i<End;i++){
    			for(int j=i;j;j-=j&-j)
    			if(check(i^(j&-j),Log[j&-j]))
    			{ans++;f[i]=1;break;}
    		}
    		cout<<ans<<'
    ';
    	}
    }
    

    满分做法

    满分做法需要用到竞赛图的一个性质:(a_{i,j}hat{} a_{i,j}=1(i ot=j))

    先维护一个 (g[i]) 表示 (i) 里面的点的出边指向的点的交集,设 (g[0]=(111..111)_2)

    (j) 为集合 (g[i]) 的子集,对于每一个合法状态 (i) ,将 (i|j) 标记为不合法。

    因为那个性质:若有一条 (x) 指向 (y) 的边,则没有 (y) 指向 (x) 的边。

    所以 (j) 中的点都没有指向 (i) 中的点的边,那么 (i|j) 这个图就肯定不连通。

    #include<bits/stdc++.h>
    using namespace std;
    int T,n,dis[30][30],ans,road[30],g[16778000],Log[16778000];
    bool f[16778000];
    inline int read()
    {
    	int x=0,w=0;char ch=0;
    	while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return w?-x:x;
    }
    int main()
    {
    	T=read();
    	for(int i=1;i<=24;i++)Log[1<<(i-1)]=i;
    	while(T--){
    		n=read();
    		for(int i=0;i<=n;road[i++]=0);
    		for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			road[i]|=read()<<(j-1);
    		g[0]=-(ans=1);
    		for(int i=0,End=1<<n;i<End;f[i++]=0);
    		for(int i=1,End=1<<n;i<End;i++){
    			g[i]=g[i^(i&-i)]&road[Log[i&-i]];
    			if(f[i])continue;
    			ans++;
    			for(int j=g[i];j;j=(j-1)&g[i])f[i|j]=1;
    		}
    		cout<<ans<<'
    ';
    	}
    }
    
  • 相关阅读:
    sed思维导图
    Golang计算文件MD5
    grpc入门
    解决使用fastjson时,null值在转JSONObject时丢失的问题
    ArcGIS空间分析案例教程网格和标记生成
    在 VirtualBox VM 中安装 OpenWRT
    aws中各个系统的用户名
    emacs简单配置和scheme环境
    Vue 中引入 json 的三种方法
    Lodash节流和防抖总结
  • 原文地址:https://www.cnblogs.com/zYzYzYzYz/p/13848319.html
Copyright © 2020-2023  润新知