1 条题解
-
1
这个题正向思考很难,正难则反
考虑原图的补图,可以证明: 若满足题目要求,补图的每一个连通块都是完全图
在原图上,若 有边,则对于任意一个点 ,使得 和 有至少有一个连了边。
在补图上的是逆命题:若 没有边,则存在一个点,使得 有边 或 有边。
逆命题是假命题,那么逆否命题就是真命题:在补图上,若 有边,则对于任意的一个点 ,到 和 一定只有 条边或 条边。
可等价转化为:补图的每一个连通块都是完全图
注意到 ,可以用状态压缩dp
考虑设 表示:将集合 里的点变成合法的所需的最少的步数
枚举每一个 是 的子集,使得将 里的所有点变成一个完全图时的最小步数
你可以预处理 ,为将集合 里的点变成完全图的步数
这样的话就有一个显然的转移方程:
代码很好写,剩下的注释在解释里:
#include<iostream> #include<cstdio> using namespace std; inline int read(){ int x=0; char c=getchar(); while (c<'0' || c>'9'){ c=getchar(); } while (c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x; } int n,m,f[1<<17],e[17][17],c[1<<17]; int main(){ n=read(); m=read(); for (int i=1;i<=m;i++){ int a=read(), b=read(); e[a][b]=e[b][a]=1;//你可以假装这是反图 } for (int S=0;S<(1<<n);S++){//预处理出 C[S] for (int i=1;i<=n;i++){ if (!(S&(1<<(i-1)))) continue; for (int j=i+1;j<=n;j++){ if (S&(1<<(j-1))) c[S]+=e[i][j]; //连边 else c[S]+=(e[i][j]^1); //删边 } } } for (int S=0;S<(1<<n);S++){ f[S]=c[S]; for (int T=S;T;T=(T-1)&S){ f[S]=min(f[S], f[S^T]+c[T]); } } printf("%d",f[(1<<n)-1]); return 0; }
- 1
信息
- ID
- 97
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- (无)
- 递交数
- 23
- 已通过
- 6
- 上传者