D. Shortest Cycle(無向圖求最小環) AND HDU6736 Forest Program

題目鏈接:https://codeforces.com/contest/1206/problem/D
在這裏插入圖片描述分析:
經過思考,很容易發現若同一位的 1 的數量 >= 3 ,則答案直接爲3,否則點的數量不會超過(2*64,除0外)
然後直接求最小環就行了,比較常見的算法有 floyed 和 dfs.
floyed:

#include<vector>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
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);
        }
}

仔細分析會發現這個思路是有問題的,比如有一個圖,如下

我首先搜點1,然後依次搜到 2 ,3,4,5,6,這時候就有一個答案5,記錄,繼續搜到 7,又有一個答案7,但是通過肉眼觀察發現應該還有一個答案 4,但是並沒有出現這個答案,爲什麼呢?
仔細分析發現原因是因爲 7進入的位置應該是2的,但是因爲一路搜過來,搜到他了,因此你後面不搜他,會漏掉很多答案,因此在吧7這個點搜一遍就行了。
dfs:

#include<vector>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
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,則答案爲
2bi=1k(2ci1)2^{b}\prod_{i=1}^{k}(2^{c_{i}}-1)
因爲題目說了,沒有重邊並且每條邊只能參加一個環,直接DFS就行,甚至比上面還簡單。

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
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
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章