题意:给一个无向图,如果
u
,
v
u,v
u,v有边相连,那么他们的边权为1,否则为0,求最小生成树
解法:不难发现最小生成树权值其实可以转化为该无向图的补图的联通块个数减一,那我们就来求补图联通块好了,我们用一个
s
e
t
set
set存所有没有用过的点,我们开始任意选择一个
s
e
t
set
set里的点开始
b
f
s
bfs
bfs,假设出队的点为
u
u
u,我们枚举
s
e
t
set
set的点
v
v
v,如果
u
,
v
u,v
u,v没有边,那么
u
,
v
u,v
u,v就是一个联通块,把
v
v
v加入队列即可,并且从
s
e
t
set
set中删除
v
v
v即可,如果有边,则继续枚举即可
#include<bits/stdc++.h>
using namespace std;constint maxn =1e5+10;int n, vis[maxn], cnt[maxn], res;
set<int> s, G[maxn];
queue<int> q;voidbfs(int u){
vis[u]=1;
s.erase(u);
q.push(u);while(!q.empty()){
u = q.front();
q.pop();
set<int>::iterator it;for(it = s.begin(); it != s.end();){int v =*it;++it;if(G[u].find(v)== G[u].end()){
s.erase(v);
vis[v]=1;
q.push(v);}}}}intmain(){int m, u, v, ans =0;scanf("%d%d",&n,&m);for(int i =1; i <= n; i++)
s.insert(i);for(int i =1; i <= m; i++){scanf("%d%d",&u,&v);
G[u].insert(v);
G[v].insert(u);}for(int i =1; i <= n; i++)if(!vis[i]){
ans++;bfs(i);}printf("%d\n", ans -1);}
题意:有
n
n
n个数字集合(所有数都不同),你要从每个集合取出一个数字,然后重新分配给每个集合一个数字,使得每个集合的数字和相等,输出选择方案。
解法:我们先求出平均每个集合数字和
s
u
m
sum
sum,然后把每个数字看成一个点,枚举每个数字
a
i
j
a_{ij}
aij,假设第
i
i
i个集合的数字和为
s
i
s_{i}
si,如果我把
a
i
j
a_{ij}
aij取出去,那么需要补充
v
a
l
=
s
u
m
−
(
s
i
−
a
i
j
)
val = sum-(s_{i}-a_{ij})
val=sum−(si−aij),如果
v
a
l
val
val存在,且
v
a
l
val
val不在
i
i
i集合或者
v
a
l
=
a
i
j
val=a_{ij}
val=aij,那么我们可以连一条有向边
a
i
j
−
>
v
a
l
a_{ij}->val
aij−>val,问题转化成了
n
n
n个集合能否组成一个或多个不相交的环,由于
n
<
=
15
n<=15
n<=15,我们可以先暴力找到所有环包含的集合,然后用状压
d
p
dp
dp去解即可。
#include<bits/stdc++.h>#define pi pair<int, int>#define mk make_pair#define ll long long
using namespace std;constint maxn =5001*15;
unordered_map<ll,int> mp;
vector<pi> ans[1<<15];int cnt, dp[1<<15];int k[16], a[16][5001], vis[16], G[maxn];
ll s[16];
pi p[maxn];voidgao(int jie){memset(vis,0,sizeof(vis));
queue<int> q;
vector<pi> tmp;int S =(1<< p[jie].first -1);
q.push(jie);while(!q.empty()){int u = q.front();
q.pop();if(u == jie && vis[p[u].first]){
dp[S]=1;
ans[S]= tmp;return;}if(vis[p[u].first])return;
vis[p[u].first]=1;if(G[u]){int v = G[u];
q.push(v);
S |=(1<< p[v].first -1);
tmp.push_back(mk(a[p[v].first][p[v].second], p[u].first));}}}
bool cmp(pi A, pi B){int p1 = mp[A.first];int p2 = mp[B.first];return p[p1].first < p[p2].first;}intmain(){int n;scanf("%d",&n);
ll sum =0;for(int i =1; i <= n; i++){scanf("%d",&k[i]);for(int j =1; j <= k[i]; j++){scanf("%d",&a[i][j]);
sum += a[i][j];
s[i]+= a[i][j];
mp.emplace(a[i][j],++cnt);
p[cnt]=mk(i, j);}}if(sum % n)returnputs("No"),0;
sum /= n;for(int i =1; i <= n; i++)for(int j =1; j <= k[i]; j++){
ll v = sum -(s[i]- a[i][j]);int cur = mp[v];if(!cur ||(p[cur].first == i && p[cur].second != j))continue;
G[mp[a[i][j]]]=(cur);}for(int i =1; i <= cnt; i++)gao(i);int S =(1<< n)-1;for(int i =1; i <= S; i++)if(!dp[i]){for(int j =(i -1)& i; j; j =(j -1)& i)if(dp[j]&& dp[i ^ j]){
dp[i]=1;
ans[i]= ans[j];for(auto tmp : ans[i ^ j])
ans[i].push_back(tmp);break;}}if(dp[S]==0)returnputs("No"),0;puts("Yes");sort(ans[S].begin(), ans[S].end(), cmp);for(auto tmp : ans[(1<< n)-1])printf("%d %d\n", tmp.first, tmp.second);}