tc-srm704-div1-1000 解题报告

来源:互联网 发布:淘宝 户外头巾 编辑:程序博客网 时间:2024/04/28 23:00

题意:构造一个点数不超过20无重边无自环的有向图,节点编号从0到n-1,使得从0到n-1的哈密顿路径数量恰好为k。

感觉完全没有思路。。不知道怎么构造。
去看了下别人的代码,原来可以做成1<-2<-…<-18一条路径,然后1-18都向编号比它大的节点连边。这样的话往回走就只有唯一的路径,所以假如说我当前在i,走到了j,j>i,那么就需要立刻从j到j-1直到i+1,然后再往后走。这样就相当于每个节点有选或不选两种状态,如果选的话就是从前面第一个比它小的选的节点跳过来,不选的话就是从i+1过来。所以如果有0->i,就相当于贡献了218i11i<18)(因为18是必选的,19不能回到18)。

既然不会做的话,那就乱搞一下吧~
假如说随便确定了一个图,怎么求路径数量呢?那就需要O(220202)的状压dp。不过我们可以先不求0->n-1的哈密顿路径数量,可以先求i>19,1i<19,然后再连从0出去的边,这样的话就相当于是问是否存在一个子集和恰好为k。
我们知道(189)是一个非常大的数了,所以如果i->19的路径数量是在k9附近随机,那么就很容易一不小心就凑出个k来!所以我们可以二分边的总数,直到找到一个边的总数在那里随机i>19的路径数量的平均值会离k9比较近,然后就在那里一直随机。
这样的话时间复杂度是O(2nn2logn)109的。所以dp的常数比较要用心卡一下才行。。。我一开始算错了时间复杂度以为是O(2nnlogn)的就没管,结果样例都过不去。。。

代码:

#include<bits/stdc++.h>using namespace std;const int n=20,N=20;typedef long long LL;class HamiltonianConstruction{    public:        pair<int,int> edge[N*N];        int etot;        LL f[1<<N][N];        LL s[1<<N];        int prev[N];        int bit[1<<N];        inline void cal(int mid)        {            random_shuffle(edge,edge+etot);            memset(prev,0,sizeof(prev));            for(int i=mid;i--;)prev[edge[i].second-1]|=1<<edge[i].first-1;            memset(f,0,sizeof(f));            f[1<<n-2][n-2]=1;            for(int i=1;i<1<<n-1;++i)                for(int j=i,x;j;j^=1<<x)                    if(f[i][x=bit[j&-j]])                        for(int k=prev[x]&~i,y;k;k^=y)                        {                            y=k&-k;                            f[i|y][bit[y]]+=f[i][x];                        }            for(int i=n-2;i--;)s[1<<i]=f[(1<<n-1)-1][i];            for(int i=1;i<1<<n-2;++i)s[i]=s[i^i&-i]+s[i&-i];        }        inline vector<string> construct(int k)        {            for(int i=0;i<n;++i)bit[1<<i]=i;            for(int i=n-1;--i;)                for(int j=n;--j;)                    if(i!=j)                        edge[etot++]=make_pair(i,j);            vector<string> ans(N,string(N,'N'));            int l=0,r=100;            while(r-l>1)            {                int mid=l+r>>1;                cal(mid);                for(int i=1<<n-2;i--;)                    if(s[i]==k)                    {                        for(int j=mid;j--;)ans[edge[j].first][edge[j].second]='Y';                        for(int j=n-2;j--;)                            if(i>>j&1)                                ans[0][j+1]='Y';                        return ans;                    }                LL sum=0;                for(int i=n-2;i--;)sum+=s[1<<i];                if(sum>=k<<1)r=mid;                else l=mid;            }            for(;;)            {                cal(r);                for(int i=1<<n-2;i--;)                    if(s[i]==k)                    {                        for(int j=r;j--;)ans[edge[j].first][edge[j].second]='Y';                        for(int j=n-2;j--;)                            if(i>>j&1)                                ans[0][j+1]='Y';                        return ans;                    }            }        }};

总结:
①构造的时候可以考虑一些特殊情况,或者根据题目中的特殊性质。比如说构造一个图,就可以先考虑一条链、完全图、树。
②如果没有构造的思路,不妨试试随机化!
③一定要算好时间复杂度!

0 0
原创粉丝点击