zoukankan      html  css  js  c++  java
  • 【BZOJ4715】囚人的旋律 DP

    【BZOJ4715】囚人的旋律

    Description

    我们令a[i]表示看守的第i扇门对应囚犯的哪一扇门。令图G为有n个节点的图,编号为1~n。对于满足1≤i<j≤n的一对i和j,如果有a[i]>a[j],那么在G中编号为i和j的节点之间连一条边。得到的图G被称为逆序图。对于图G=(V,E),非空点集S∈V是一个独立集当且仅当对于任意两个点u,v∈V,不存在(u,v)∈E。而S是一个覆盖集当且仅当对于任意点v?S存在点u∈S满足(u,v)∈E。我们在意的是,图G中有多少个点集既是独立集又是覆盖集。出于某种不知名的原因,被迫参加监狱游戏的大家的安危和这个问题的答案有关。拜托了,请一定要求出这个方案数。

    Input

    输入第一行含有两个整数n和m,表示逆序图的点数和边数。
    接下来m行,每行描述一条边。每行包含两个1~n的整数,代表边的两个端点。保证没有重边和自环。
    保证给定的图是一个合法的逆序图,即,存在至少一个序列,按照题目描述中所述方法得到的逆序图是给定的图。
    n≤1000,0≤m≤(n(n-1))/2

    Output

    输出一个整数,表示方案数对1,000,000,007取模得到的结果。

    Sample Input

    5 5
    2 4
    2 5
    1 4
    3 4
    3 5

    Sample Output

    3

    题解:首先,根据这个逆序图是可以在O(n^2)时间内将原序列还原出来的。

    方法:统计每个数前面有多少个大于它的数pre,后面有多少个小于它的数nxt,那么最小的数i一定满足pre[i]=i-1&&nxt[i]=0,所以找出最小的数,然后将它删掉,更新其它点的pre和nxt。不断重复此过程即可。

    那么本题求的东西到底是什么呢?一开始以为求的是边覆盖集,然后就变成了将原序列分成两个不相交的上升子序列,但是样例过不去。。。后来发现求的是点覆盖集。。。

    因为选出来的序列是独立集,所以这个序列是递增的;又因为这是覆盖集,所以每个不在序列中的点都与某个序列中的点形成逆序对。所以我们求的就是极长上升序列的方案数!用f[i]表示前i个数中极长上升序列方案数,如果i是一个合法的结尾,则用f[i]更新答案即可。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int P=1000000007;
    int n,m,ans;
    int map[1010][1010],f[1010],nxt[1010],pre[1010],v[1010],bef[1010],aft[1010];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,k,a,b;
    	for(i=1;i<=m;i++)
    	{
    		a=rd(),b=rd();
    		if(a>b)	swap(a,b);
    		map[a][b]=1,nxt[a]++,pre[b]++;
    	}
    	for(i=1;i<=n;i++)	for(j=i+1;j<=n;j++)
    	{
    		if(!map[i][j])	bef[j]=aft[i]=1;
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(k=1;k<=n;k++)	if(!v[k]&&!nxt[k]&&pre[k]==k-1)	break;
    		v[k]=i;
    		for(j=1;j<k;j++)	if(map[j][k])	nxt[j]--;
    		for(j=k+1;j<=n;j++)	pre[j]++;
    	}
    	for(i=1;i<=n;i++)
    	{
    		f[i]=(f[i]+(!bef[i]))%P;
    		for(k=n+1,j=i+1;j<=n;j++)	if(!map[i][j]&&v[j]<k)	k=v[j],f[j]=(f[j]+f[i])%P;
    		if(!aft[i])	ans=(ans+f[i])%P;
    	}
    	printf("%d",ans);
    	return 0;
    }

     

  • 相关阅读:
    编译原理-第二章 一个简单的语法指导编译器-2.4 语法制导翻译
    编译原理-第二章 一个简单的语法指导编译器-2.3 语法定义
    编译原理-第二章 一个简单的语法指导编译器-2.2 词法分析
    LeetCode 1347. Minimum Number of Steps to Make Two Strings Anagram
    LeetCode 1348. Tweet Counts Per Frequency
    1349. Maximum Students Taking Exam(DP,状态压缩)
    LeetCode 1345. Jump Game IV(BFS)
    LeetCode 212. Word Search II
    LeetCode 188. Best Time to Buy and Sell Stock IV (动态规划)
    LeetCode 187. Repeated DNA Sequences(位运算,hash)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7634269.html
Copyright © 2011-2022 走看看