SRM574 Div1Medium PolygonTraversal

12 篇文章 0 订阅

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

【代码】

#include <bits/stdc++.h>
using namespace std;
#define M 18
#define ll long long
ll dp[M][1<<M];
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<<cur)){
            l=1;
            break;
        }
        else cur=(cur+1)%n;
    }
    cur=(now+1)%n;
    while(cur!=to){
        if(mk[cur]||si&(1<<cur)){
            r=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;i<n;i++){
        if(i==p||mk[i]||(si&(1<<i)))continue;
        if(chk(p,i,si))res+=DP(i,si|(1<<i),cnt+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<<a[i]);
    }
    cout<<DP(a[m],hav,m)<<endl;
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值