求简单无向图中环的个数?
简单无向图即 无自环,无重边的无向图。
这个是NP问题; codeforces 上有一题用的是状态压缩写的。
dp[s][i] s集合里最小的点到其他点的路径数;
dp[s][i] += dp[s^(1<<i)]j
ans加上可以构成环的路径数.怎么才能构成环呢? 如a->b->…->c ,如果知道ac是可达的,只要加上a,经过ab…到达c的路径数就可以了。注意a是这个集合里最小的数。而且同一个环会被记录两次,因为2条路径才是一个环。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef __int64 ll;
#define maxn 20
ll dp[1<<maxn][maxn]; // 注意数据
bool g[maxn][maxn];
// dp[s][i] s中最小的点到其他点路径数;
// dp[s][i] += dp[s^i][j](g[i][j] = true )
int main(){
int n, m;
while( cin >> n >> m ) {
memset(g, 0, sizeof g);
int state = (1<<n);
for ( int i=0; i<state; ++i)
for ( int j=0; j<n; ++j )
dp[i][j] = 0;
for ( int i=0, a, b; i<m; ++i ){
scanf ("%d%d", &a, &b);
--a, --b;
g[a][b] = g[b][a] = true;
dp[(1<<a)|(1<<b)][a] = dp[(1<<a)|(1<<b)][b] = 1;
}
ll ans = 0;
for ( int s=1; s<state; ++s ){
int i, j, k;
for ( i=0; i<n && !(s&(1<<i)); ++i );
for ( j=i+1; j<n; ++j ) if(s&(1<<j) )
{
for ( k=i+1; k<n; ++k ) if(s&(1<<k)){
if(g[k][j] )
dp[s][j] += dp[s^(1<<j)][k];
}
if(g[i][j] && (s^(1<<i)^(1<<j))) // 3个点以上才行
ans += dp[s][j];
}
}
// 枚举了环的两侧,so。。。
cout << (ans>>1) << endl;
}
};
求最小环路径,任意一条最小环的路径。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 108
int n, m;
int dist[maxn][maxn], g[maxn][maxn];
int pre[maxn][maxn]; // 路径
#define INF 1<<24
vector<int > ans;
void init(){
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
dist[i][j]=g[i][j]=INF, pre[i][j]=i;
int u, v, w;
for(int i=0; i<m; ++i ) {
scanf("%d%d%d", &u, &v, &w);
if(w < dist[u][v] ){
dist[u][v] = dist[v][u] = w;
g[u][v] = g[v][u] = w;
}
}
};
void solve(){
int minn = INF; ans.clear();
for(int k=1; k<=n; ++k )
{
// 枚举两条边,点号小于k
for(int i=1; i<k; ++i) if(g[i][k]^INF)
for(int j=i+1; j<k; ++j) if(g[k][j]^INF && dist[i][j]^INF) {
int tmp = dist[i][j]+g[i][k]+g[k][j];
if(tmp < minn) { // 求出路径
minn = tmp; ans.clear();
int p = j;
while(p != i) {
ans.push_back(p);
p = pre[i][p];
}
ans.push_back(i);
ans.push_back(k);
}
}
for(int i=1; i<=n; ++i ) if(dist[i][k]^INF)
for(int j=1; j<=n; ++j )if(dist[k][j]^INF) {
int tmp = dist[i][k]+dist[k][j];
if(tmp < dist[i][j]) {
dist[i][j]= tmp;
pre[i][j] = pre[k][j]; // 从后往前
}
}
}
if(minn ^ INF) {
//cout<< minn << endl;
for(int i=0; i<ans.size(); ++i){
if(i) printf(" "); printf("%d", ans[i]);
}
puts("");
return ;
}
puts("No solution.");
};
int main(){
while(~scanf("%d%d", &n, &m)){
init();
solve();
}
};
求最大环路径,任意一条最大环的路径。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxm = 100005;
const int maxn = 27*27;
int n, head[maxn], e, N;
struct Edge{
int u, v, next; double w;
Edge(){}
Edge(int U, int V, int Ne, double W):
u(U), v(V), next(Ne), w(W){}
}edge[maxm];
void add(int u, int v, double w){
edge[e] = Edge(u, v, head[u], w); head[u] = e++;
}
int mm[30][30];
int get( char x, char y) {
int i = x-'a', j = y-'a';
if(mm[i][j] == 0) mm[i][j]=++N;
return mm[i][j];
}
double maxLength;
void init(){
char str[1071];
e = 0; N = 0; maxLength = 0; //maxLength 最大的边
memset(head, -1, sizeof head); //开始这里我用的是fill,悲剧的rt了如干次啊
memset(mm, 0, sizeof mm);
getchar();
for(int i=0; i<n; ++i ) {
scanf("%s", str);
int sz = strlen(str);
if(sz < 3) continue;
int u = get(str[0], str[1]);
int v = get(str[sz-2], str[sz-1]);
add(u, v, sz); //每个字串见条边就可以了
if(sz > maxLength) maxLength = sz;
}
}
double dist[maxn];
int cnt[maxn];
bool vis[maxn];
int Q[maxn];
bool spfa( double x ) {
fill(dist+1, dist+1+N, 0);
fill(cnt+1, cnt+1+N, 0 );
memset(vis, 0, sizeof vis);
int l=0, r=0;
for(int i=1; i<=N; ++i)
Q[r++] = i, vis[i]=1;
while(l != r) {
int u = Q[l++]; if(l == maxn) l=0; vis[u]=0;
for ( int i=head[u]; ~i; i=edge[i].next){
int v = edge[i].v; double w = edge[i].w;
if(dist[u]+w-x > dist[v]){
dist[v] = dist[u]+w-x;
if(vis[v] ) continue;
Q[r++] = v; vis[v]=1;
if(r == maxn) r=0;
if(++cnt[v] > N) return 1; // 存在正环
}
}
}
return 0;
}
#define eps 1e-4
void solve()
{
double l = 0, r = maxLength, mid, ans=-1;
while(r-l >= eps){// 二分球结果
mid = (l+r)/2.0;
if(spfa( mid ) ) {
ans = mid;
l = mid;
}else r = mid;
}
if(ans > eps) printf("%.2lf\n", ans);
else puts("No solution.");
};
int main(){
while( scanf("%d", &n), n ){
init();
solve();
}
}
转载自:https://www.cnblogs.com/TengXunGuanFangBlog/archive/2013/04/19/loop_problem.html