SRM574 Div1Medium PolygonTraversal

【分析】
看到题目的范围时,我就在想这肯定是状压dp,肯定是定义dp[i][j]表示此时在i,取得点的集合为j。
那么问题就来了,有这些点,我怎么来判断从i是否能到j的补集的点呢?接下来我就在纸上画了图。
最后我发现,如果x想要到y,那么在圆上x顺时针到y或x逆时针到y一定都要有点。(至于为什么,我怎么知道,实践出真知)。
那么问题就解决了!

【代码】

#include 
using namespace std;
#define M 18
#define ll long long
ll dp[M][1<bool mk[M];
int a[M];
int hav;
int n,m;
bool chk(int now,int to,int si){
    int cur;
    bool l=0,r=0;
    cur=(to+1)%n;//我这个写法比较神奇,这样now到to就不用逆时针旋转了,to只需要顺时针到now,本质是一样的,只是不需要判一些东西。
    while(cur!=now){
        if(mk[cur]||si&(1<1;
            break;
        }
        else cur=(cur+1)%n;
    }
    cur=(now+1)%n;
    while(cur!=to){
        if(mk[cur]||si&(1<1;
            break;
        }
        else cur=(cur+1)%n;
    }
    return l&&r;
}
ll DP(int p,int si,int cnt){
    ll &res=dp[p][si];
    if(res)return res;
    if(cnt==n)return res=chk(p,a[1],si);
    res=0;
    for(int i=0;iif(i==p||mk[i]||(si&(1<continue;
        if(chk(p,i,si))res+=DP(i,si|(1<1);
    }
    return res;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&a[i]);
        a[i]--;
        mk[a[i]]=1;
        hav+=(1<cout<return 0;
}

你可能感兴趣的:(TC,dp)