题目大意:
一个序列,所有逆序对之间都有一条边,问有多少个子集是个独立集,而且不属于集的全部都和集中至少有一条边。
题目思路:
首先逆序对之间一定有边啊,所以呢,这个子集一定是个上升子序列,但是上升子序列看样例好像还是不对劲,为啥呢比如这个2 3 5 ,如果我们只选择了3 5,的话,2就和集合失去了联系,所以我们要选择怎样的上升子序列呢,仔细观察可以发现最小元素之前不能出现更小的元素,因为加入之前有一个更小的元素那么,这个元素和集合中的必然没有关系,同样的,子序列的最后一个元素在原序列中后边不能出现比这个元素更大的值,要不然这个就失去联系了。
所以我们可以dp【i】表示以i为结尾的这样的上升子序列有多少个,什么情况下dp【i】可以由dp【j】转移得到呢,首先有一个条件,那就是j比i对应的数字要大,上升子序列嘛,既然是以i结尾那么 1-j的自然不用考虑,但是j - i的这一段都要考虑能否和集合有联系。怎么判断呢,只要和i,j之间的一个有边就可以了。
最后答案怎么统计呢,并不是每一个满足条件的都可以作为答案对吧,要判断一下i之后的是不是没有比i大的数字了, 才可以做答案贡献。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN = 105;
ll dp[MAXN],G[MAXN][MAXN];
int main()
{
ll n,m;
while(~scanf("%I64d%I64d",&n,&m))
{
memset(G,0,sizeof(G));
memset(dp,0,sizeof(dp));
for(ll i=1;i<=m;i++){
ll a,b;
scanf("%I64d%I64d",&a,&b);
G[a][b]++;
G[b][a]++;
}
dp[0] = 1;
for(ll i=1;i<=n;i++){
for(ll j=0;j<i;j++){
bool flag = 1;
if(G[i][j])continue;
for(ll k=j+1;k<i;k++){
if(G[i][k]||G[j][k])continue;
else{
flag = 0;break;
}
}
dp[i] += flag * dp[j];
}
}
ll ans = 0;
for(int i=1;i<=n;i++){
bool f = 1;
for(int j=i+1;j<=n;j++){
if(!G[i][j]){
f=0;
}
}
ans += f*dp[i];
}
printf("%I64d\n",ans);
}
}