題目鏈接: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,則答案爲
因爲題目說了,沒有重邊並且每條邊只能參加一個環,直接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
*/