牛客国庆集训派对Day2 E 数据排序(状态压缩dp)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82928537

虽然说得分可以一样,但是我们还是可以仅仅只用两个状态0和1来表示这一题。

某一个位置为0表示当前这个数字还没有被标号,也即当前数字比所有的已经标号的数字都要小。如果为1,那么说明这个已经标号,并且这个数字与比它先标号的数字和与其相等的数字的冲突值已经计算过了。于是一个状态,我们可以用一个15为的二进制数字来表示。

令dp[i]表示当前的状态,那么当前状态就可以转移到后面的状态。对于后一个状态,相当于确定有哪些没有被标号的数字会在下一次被标号。因此我们可以这个下次被标号的数字的集合s,对于s里面每一个元素,在转移的时候加上它与所有比它先标号的数字的冲突值。同时也要加上,这个集合s里面所有数字标号相等时的冲突值。这样,有转移方程:

                             \large dp[i|s]=min(dp[i|s],dp[i]+w[s]+b[s])

其中的w[s]表示新加入的这么多点与之前加入的点的冲突值的和,b[s]表示s这么多点一起加入的冲突值。具体在实现的时候,可以考虑预处理出单独每一个数字加入的代价,然后在枚举子集的时候倒着来。另外,在计算单个数字的代价的时候,可以用上lowbit来加速。这样,在不算子集个数的情况下复杂度是O(N*2^N)的,实际算上自己个数之后,计算次数不会超过1500W。这样优化常数之后,最快可以到80ms,排名Rank1,具体见代码:

#include<bits/stdc++.h>
#define PI 3.1415926535
#define mod 998244353
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;

const int N = 1<<15;

int dp[N],b[N],lg[N],st[N],p[N];
int w[15],t[15][15],n;

int main()
{
    sf(n); int up=(1<<n)-1;
    for(int i=0;i<=up;i++)
    {
        dp[i]=INF;
        for(int j=0;j<n;j++)
            if (i&(1<<j)) lg[i]=j;
    }
    for(int i=1;i<=2*n*(n-1);i++)
    {
        int x,y;
        sf(x); sf(y);
        x--; y--; t[x][y]++;
    }
    for(int i=0;i<=up;i++)
        for(int j=i,l=j&-j;j;j-=l,l=j&-j)
            for(int k=j-l,ll=k&-k;k;k-=ll,ll=k&-k)
                b[i]+=abs(t[lg[l]][lg[ll]]-t[lg[ll]][lg[l]]);
    dp[0]=0;
    for(int i=0;i<up;i++)
    {
        int res=up^i;
        for(int j=res,l=j&-j;j;j-=l,l=j&-j)
        {
            w[lg[l]]=0;
            for(int k=i,ll=k&-k;k;k-=ll,ll=k&-k)
                w[lg[l]]+=t[lg[ll]][lg[l]];
        }
        int tt=0;
        for(int s=res;s;s=(s-1)&res) st[tt++]=s;
        while(tt--)
        {
            int tmp=st[tt];
            p[tmp]=p[tmp^(1<<lg[tmp])]+w[lg[tmp]];
            dp[i^tmp]=min(dp[i^tmp],dp[i]+p[tmp]+b[tmp]);
        }
    }
    printf("%d\n",dp[up]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82928537