P r o b l e m \mathrm{Problem} Problem
给出 N 个点M 条边的无向图,定向得到有向无环图,使得最长路最短。
S o l u t i o n \mathrm{Solution} Solution
现在有一个定理叫做dilworth定理。具体的内容是这样的:
- 最长路径(点的个数) = 最小反链划分
一个集合为反链表示这个集合任意两点不能连通。最小反链划分表示整个图最少由多少个反链来划分。其中这张图是确定的,因此最小反链划分唯一。
由于这道题中,我们由于需要将图定向以后,得到最小反链划分的最小值,我们需要通过状压DP解决。
设 f [ i ] f[i] f[i]表示状态i是否是一个反链, g [ i ] g[i] g[i]表示对于状态i划分的最少反链数。
这样就有: g [ i ] = g [ i ′ ] + 1 , f [ i ] = 1 , i ′ ⊆ i g[i]=g[i']+1,f[i]=1,i'\subseteq i g[i]=g[i′]+1,f[i]=1,i′⊆i.
当然还有一个结论是这样的:
- 最长反链 = 最小路径覆盖
- 其对应的例子就是导弹拦截的第二问,问多少个系统能够覆盖完所有导弹。
然后就有状态转移方程:
#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int m;
int cnt = 0, n = 0;
int mp[50][50], t[50], f[1<<20], g[1<<20];
map<char,int>num;
int main(void)
{
freopen("exclusive.in","r",stdin);
freopen("exclusive.out","w",stdout);
cin>>m;
for (int i=1;i<=m;++i)
{
char a, b; cin>>a>>b;
if (!num[a]) num[a] = ++n;
if (!num[b]) num[b] = ++n;
mp[num[a]][num[b]] = mp[num[b]][num[a]] = 1;
}
for (int i=0;i<1<<n;++i)
{
int t[50], k = 0;
f[i] = 1;
for (int j=1;j<=n;++j)
if ((i >> j-1) & 1) t[++k] = j;
for (int u=1;u<k;++u)
for (int v=u+1;v<=k;++v)
if (mp[t[u]][t[v]]) f[i] = 0;
}
memset(g,30,sizeof f);
g[0] = 0;
for (int i=0;i<1<<n;++i)
{
if (f[i]) g[i] = 1;
for (int j=i;j;j=(j-1)&i)
if (f[j]) g[i] = min(g[i],g[i^j]+1);
}
if (g[(1<<n)-1] > 1e5) puts("Nie");
else cout<<g[(1<<n)-1]-2<<endl;
return 0;
}