Problem I Inversion (dp)

题目大意:

   一个序列,所有逆序对之间都有一条边,问有多少个子集是个独立集,而且不属于集的全部都和集中至少有一条边。

题目思路:

      首先逆序对之间一定有边啊,所以呢,这个子集一定是个上升子序列,但是上升子序列看样例好像还是不对劲,为啥呢比如这个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);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值