D. Shortest Cycle(无向图求最小环) AND HDU6736 Forest Program

题目链接:https://codeforces.com/contest/1206/problem/D
D. Shortest Cycle(无向图求最小环) AND HDU6736 Forest Program_第1张图片分析:
经过思考,很容易发现若同一位的 1 的数量 >= 3 ,则答案直接为3,否则点的数量不会超过(2*64,除0外)
然后直接求最小环就行了,比较常见的算法有 floyed 和 dfs.
floyed:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e3+5;
const int INF=1e8;
#define LL long long
int n,m,ans=INF;
int p[N],s[N];
LL a[N*N];
int d[N][N];
int f[N][N];
void floyed(){
     
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            f[i][j]=d[i][j];
    for(int k=1;k<=n;k++)
    {
     
        for(int i=1;i<k;i++)
            for(int j=i+1;j<k;j++)
                if(f[i][j]+d[j][k]+d[k][i]<ans)
                    ans=f[i][j]+d[j][k]+d[k][i];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(f[i][j]>f[i][k]+f[k][j])
                    f[i][j]=f[i][k]+f[k][j];
    }
}
int main(){
     
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
    for(int i=1;i<=n;i++)
        for(int j=0;j<63;j++)
            if(a[i]&(1LL<<j))
                p[j]++;
    for(int i=0;i<63;i++)if(p[i]>=3)return puts("3"),0;
    int len=0;
    for(int i=1;i<=n;i++)
        if(a[i])a[++len]=a[i];
    n=len;

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i]&a[j])
                d[i][j]=1;
            else
                d[i][j]=INF;

    floyed();
    if(ans==INF)puts("-1");
    else printf("%d\n",ans);
}


dfs思路很好想,却很容易写错(我就是问群友怎么写的,群友NB)
易错点:
1.图未联通,只搜第一个点
2.实现出错
最初很容易想到的一个思路是:从点1开始搜,然后记录他们进入的位置,然后一路搜下去(已经走过的点不在走),判断他们是否在走到这个点,走到这个点肯定是有环的,代码如下

void dfs(int u,int rd){
     
    s[u]=rd;
    for(int i=1;i<=n;i++)
        if(d[u][i]){
     
            if(s[i]){
     
            	if(s[u]-s[i]+1>=3)
	                ans=min(ans,s[u]-s[i]+1);
	         }
			else 
				dfs(i,rd+1);
        }
}

仔细分析会发现这个思路是有问题的,比如有一个图,如下

D. Shortest Cycle(无向图求最小环) AND HDU6736 Forest Program_第2张图片

我首先搜点1,然后依次搜到 2 ,3,4,5,6,这时候就有一个答案5,记录,继续搜到 7,又有一个答案7,但是通过肉眼观察发现应该还有一个答案 4,但是并没有出现这个答案,为什么呢?
仔细分析发现原因是因为 7进入的位置应该是2的,但是因为一路搜过来,搜到他了,因此你后面不搜他,会漏掉很多答案,因此在吧7这个点搜一遍就行了。
dfs:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e3+5;
const int INF=1e9;
#define LL long long
int n,m,ans=INF;
int p[N],s[N];
LL a[N*N];
bool d[N][N];
void dfs(int u,int rd){
     
    s[u]=rd;
    //printf("%d %d\n",u,rd);
    for(int i=1;i<=n;i++)
        if(d[u][i]){
     
            if(s[i] && s[u]-s[i]+1>=3)
                ans=min(ans,s[u]-s[i]+1);
            if(!s[i] || s[i]>rd+1)
                dfs(i,rd+1);
        }
}
int main(){
     
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
    for(int i=1;i<=n;i++)
        for(int j=0;j<63;j++)
            if(a[i]&(1LL<<j))
                p[j]++;
    for(int i=0;i<63;i++)if(p[i]>=3)return puts("3"),0;
    int len=0;
    for(int i=1;i<=n;i++)
        if(a[i])a[++len]=a[i];
    n=len;

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i]&a[j])
                d[i][j]=1;
    //printf("n=%d\n",n);
    for(int i=1;i<=n;i++)
        if(!s[i])dfs(i,1);
    if(ans==INF)puts("-1");
    else printf("%d\n",ans);
}

HDU6736 Forest Program

要将仙人掌变成树(或者森林),只需要保证对于仙人掌中的每个环,至少有一条边被删去即可。
设图中环的大小分别为 c1, c2, …, ck,不属于任何一个环的
边数为 b,则答案为
2 b ∏ i = 1 k ( 2 c i − 1 ) 2^{b}\prod_{i=1}^{k}(2^{c_{i}}-1) 2bi=1k(2ci1)
因为题目说了,没有重边并且每条边只能参加一个环,直接DFS就行,甚至比上面还简单。

#pragma GCC optimize(2)
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
const int N=3e5+5;
const int mod=998244353;
const int M=5e5+7;
int in[N];
int ha[M],len,sum,si;
struct node{
     
    int v,next;
    bool flag;
}E[2*M];
int head[N],cnt;
int fib[M];
inline int read(int &x){
     
    char ch;
    while((ch=getchar())>'9'||ch<'0');
    x=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        x=(x<<3)+(x<<1)+ch-'0';
}
inline void add(int u,int v){
     
    E[cnt]={
     v,head[u],0};
    head[u]=cnt++;
}
void dfs(int u,int d){
     
 //   printf("%d %d\n",u,d);
    in[u]=d;		//记录这个点的进入时间
    for(int i=head[u];~i;i=E[i].next)
        if(!E[i].flag){
     
            sum++;//边的数量
            int v=E[i].v;
            E[i].flag=1;
            E[i^1].flag=1;
            if(in[v]){
     
                ha[++len]=in[u]-in[v]+1;
                si+=in[u]-in[v]+1;      //参与换的边的数量
            }else{
     
                dfs(v,d+1);
            }
        }
}
int main(){
     
    fib[0]=1;
    for(int i=1;i<M;i++){
     
         fib[i]=fib[i-1]*2;
         while(fib[i]>mod)
            fib[i]-=mod;
    }
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
     
        len=0;cnt=0;
        memset(head,-1,(n+1)*sizeof (int));
        memset(in,0,(n+1)*sizeof (int));
        int u,v;
        for(int i=1;i<=m;i++){
     
            read(u);read(v);
            add(u,v);
            add(v,u);
        }

        int x=0;
        for(int i=1;i<=n;i++)
            if(!in[i]){
     
                sum=0;
                si=0;
                dfs(i,1);
                x+=sum-si;
            }
        int ans=fib[x];
        for(int i=1;i<=len;i++)
            ans=1ll*ans*(fib[ha[i]]-1+mod)%mod;
        printf("%d\n",ans);
    }
    return 0;
}
/**
5 5
1 1 4
1 2 3
2 2
1 2 7
2 1
*/

你可能感兴趣的:(codeforces)