转载请注明出处:http://www.cnblogs.com/TSHugh/p/8823423.html

  读完题就会发现p=0、1的情况以及n=1、2的情况都可以直接判掉,而p=2的时候也可以直接构造,那么现在需要的就是当p=3且n>=3的时候的做法.
  容易想到小数据范围下的dfs,但是这难以优化,于是去思考dp的做法.我的思路一开始是dp弧,后来发现可以直接dp两个链,但是复杂度太大,并不比dfs优秀多少.去看题解,只有Claris写了题解,他是这样写的:

p=3时不考虑1的座位进行DP
可以发现对于i+1的位置安排,我们只关心i-2,i-1,i的相对顺序以及它们的相邻、边界情况
所以设f[i][j][S1][S2]表示已经安排了前i个人的座位,i-2,i-1,i的顺序为j,是否有人在两端点为S1,是否有人相邻为S2的方案数 
答案最后再除以n
这样复杂度有点飞…

  这并没有使我满意,因为我感觉这在时间、空间、代码各方面的复杂度都是不优秀的.
  此时我看到了金策的700+ms做法,而且代码也并不长,这让我意识到此题有更加优秀的做法,于是在搜寻标程失败后去poi官网get了一发题解,然后利用google翻译了一发,得到了一个神奇的做法.
  首先,题解里说了一句话,这题实际上是在数哈密顿回路,我思考了一下,好像是这样的……然而,这题的解法和哈密顿回路并没有什么卵关系……
  转化一下问题:

I.把所有编号i变为n-i.
II.把环拆开,把原问题变成——求一段序列满足题目限制,且开头一定为0,结尾一定为1/2/3.

  这样的话,再对三种结尾判断一下取舍,就能得到最终答案了.
  对于现在的问题可以设计dp状态(好神奇的状态啊……):

f[i]:对于一段序列,开头为i,结尾为i+1,且序列中的数字均属于[i,n),此序列满足题目限制的方案数.
g[i]:对于一段序列,开头为i+1,结尾为i,且序列中的数字均属于[i,n),此序列满足题目限制的方案数.

  先看dp的转移(好厉害的转移啊……):

先贴一张图(来自波兰题解):

你看这张图,你就会懂得求解方法了,于是得到了一个递推式:f[i]=g[i+1]+g[i+2]+g[i+4]+g[i+5].
同理,也可以得到:g[i]=f[i+1]+f[i+2]+f[i+4]+f[i+5].
但是,上述方法似乎只适用于i<=n-8,所以,对于i>=n-7,我们就可以直接dfs处理了.
(注意判断额外限制条件)

  假设三种结尾的方案数分别为ans1、ans2、ans3.
  既然知道了dp的转移,那么怎么算三种ans呢?
  沿用刚才转移的思路,可以得到(图仍然来自波兰题解):

ans1=f[0];(显然)

ans2=f[1]+f[3]+f[4];(原因见上图)

ans3=f[2]+f[4]+f[5]+g[3]+g[4];(原因见上图)
(注意判断额外限制条件)

  所以对于p=3且n>=3的时候,判断一下,若n<=7,直接dfs,否则使用上述方法.
  至此,这道题就解决了,时间复杂度为O(n),实现见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(<<)+],*xS=xB,*xT=xB;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){
register char ch=gtc;
for(x=;ch<''||ch>'';ch=gtc);
for(;ch>=''&&ch<='';x=(x<<)+(x<<)+ch-'',ch=gtc);
}
const int N=;
const int Inf=0x3f3f3f3f;
const int P=;
int n,k,p,f[N],g[N];
bool NO[N][],die[N],vf[N],vg[N];
#define no(a,b) (NO[a][(b)+3])
#define ok(a,b) (!no(a,(b)-(a)))
inline int work(){
int i,x,y,ans=;
for(i=;i<=k;++i){
read(x),read(y);
if(std::abs(x-y)<=)no(x,y-x)=true;
}
for(i=;i<=n;++i)
if(i&)f[(i+)>>]=i;
else f[n-(i>>)+]=i;
f[]=f[n],f[n+]=f[];
++ans;
for(i=;i<=n;++i)
if(no(f[i],f[i+]-f[i])){
--ans;break;
}
++ans;
for(i=;i<=n;++i)
if(no(f[i],f[i-]-f[i])){
--ans;break;
}
printf("%d\n",ans);
return ;
}
inline int dfs(int pos,int last,int k,int t,int len){
if(pos==len)return std::abs(t-last)<=&&ok(last,t);
int i,ret=;
for(i=std::max(last-,k+);i<=last+&&i<n;++i)
if((!die[i])&&ok(last,i)){
die[i]=true;
ret+=dfs(pos+,i,k,t,len);
die[i]=false;
}
return ret;
}
inline int D(int s,int k,int t){
die[s]=die[t]=true;
int ret=dfs(,s,k,t,n-k);
die[s]=die[t]=false;
return ret;
}
inline int F(int x);
inline int G(int x);
inline int F(int x){
if(vf[x])return f[x];
vf[x]=true;
if(n-x<=)return f[x]=D(x,x,x+);
int ret=;
if(ok(+x,+x))ret=(ret+G(+x))%P;
if(ok(+x,+x)&&ok(+x,+x))ret=(ret+G(+x))%P;
if(ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x))ret=(ret+G(+x))%P;
if(ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x))ret=(ret+G(+x))%P;
return f[x]=ret;
}
inline int G(int x){
if(vg[x])return g[x];
vg[x]=true;
if(n-x<=)return g[x]=D(x+,x,x);
int ret=;
if(ok(+x,+x))ret=(ret+F(+x))%P;
if(ok(+x,+x)&&ok(+x,+x))ret=(ret+F(+x))%P;
if(ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x))ret=(ret+F(+x))%P;
if(ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x)&&ok(+x,+x))ret=(ret+F(+x))%P;
return g[x]=ret;
}
inline int Work(){
int i,x,y,d=,ans=;
for(i=;i<=k;++i){
read(x),read(y);
x=n-x,y=n-y;
if(std::abs(x-y)<=)no(x,y-x)=true;
}
if(n<=){
if(ok(,))ans=(ans+D(,,))%P;
if(ok(,))ans=(ans+D(,,))%P;
if(n>=&&ok(,))ans=(ans+D(,,))%P;
printf("%d\n",ans);
return ;
}
for(i=n-d;i>d;i-=d)G(i),F(i);/*为了防止爆栈和MLE*/
if(ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+F())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+G())%P;
if(ok(,)&&ok(,)&&ok(,)&&ok(,)&&ok(,))ans=(ans+G())%P;
printf("%d\n",ans);
return ;
}
int main(){
//freopen("cza.in","r",stdin);
//freopen("cza.out","w",stdout);
read(n),read(k),read(p);
if(n==)return puts(""),;
if(p==)return puts(""),;
if(n==)return puts(k?"":""),;
if(p==)return puts(""),;
if(p==)return work(),;
return Work(),;
}

Kod

BZOJ #3746: [POI2015]Czarnoksiężnicy okrągłego stołu 动态规划的更多相关文章

  1. BZOJ3746 : [POI2015]Czarnoksiężnicy okrągłego stołu

    NOIP前做了几道POI,现在终于能在BZOJ上提交了… 交上去最后几个点WA,看了数据发现p=0的特判错了… p=0,1时特判 p=2时构造两种情况判断 p=3时不考虑1的座位进行DP 可以发现对于 ...

  2. BZOJ 4276: [ONTAK2015]Bajtman i Okrągły Robin [线段树优化建边]

    4276: [ONTAK2015]Bajtman i Okrągły Robin 题意:\(n \le 5000\)个区间\(l,r\le 5000\),每个区间可以选一个点得到val[i]的价值,每 ...

  3. BZOJ 4276: [ONTAK2015]Bajtman i Okrągły Robin

    最大权值匹配,贪心匈牙利即可. 检查一些人是否能被全部抓住可以采用左端点排序,右端点优先队列处理. By:大奕哥 #include<bits/stdc++.h> using namespa ...

  4. BZOJ 4276 [ONTAK2015]Bajtman i Okrągły Robin 费用流+线段树优化建图

    Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢 ...

  5. bzoj 4276: [ONTAK2015]Bajtman i Okrągły Robin【线段树+最大费用最大流】

    --因为T点忘记还要+n所以选小了所以WA了一次 注意!题目中所给的时间是一边闭一边开的区间,所以读进来之后先l++(或者r--也行) 线段树优化建图,很神.(我记得还有个主席树优化建树的?)首先考虑 ...

  6. [ONTAK2015]Bajtman i Okrągły Robin

    bzoj 4276: [ONTAK2015]Bajtman i Okrągły Robin Time Limit: 40 Sec  Memory Limit: 256 MB Description 有 ...

  7. bzoj 4386: [POI2015]Wycieczki

    bzoj 4386: [POI2015]Wycieczki 这题什么素质,爆long long就算了,连int128都爆……最后还是用long double卡过的……而且可能是我本身自带大常数吧,T了 ...

  8. Bajtman i Okrągły Robin

    Bajtman i Okrągły Robin 题目描述 你是一个保安,你发现有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i] ...

  9. 4276: [ONTAK2015]Bajtman i Okrągły Robin

    4276: [ONTAK2015]Bajtman i Okrągły Robin Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 345  Solved ...

随机推荐

  1. tensorflow中tensor与数组之间的转换

    # 主要是两个方法: # 1.数组转tensor:数组a, tensor_a=tf.convert_to_tensor(a) # 2.tensor转数组:tensor b, array_b=b.eva ...

  2. git remote add origin错误

    如果输入$ Git remote add origin git@github.com:djqiang(github帐号名)/gitdemo(项目名).git 提示出错信息:fatal: remote ...

  3. Vue学习计划基础笔记(六) - 组件基础

    组件基础 目标: 掌握组件的构建方式 掌握如何复用组件.父子组件如何传值.如何向父组件发送消息 掌握如何通过插槽分发内容 了解解析dom模板时的注意事项 了解动态组件 组件 组件理解起来大概上就和ph ...

  4. sqoop安装与简单实用

    一,sqoop安装 1.解压源码包 2.配置环境变量 3.在bin目录下的 /bin/configsqoop 注释掉check报错信息 4.配置conf目录下 /conf/sqoop-env.sh 配 ...

  5. linux上open-vswitch安装和卸载

    一. ovs 从源码编译安装: 安装依赖项: # apt-get install make # apt-get install gcc # apt-get install build-essentia ...

  6. day09,10 函数

    一.函数 什么是函数 函数: 对代码块和功能的封装和定义 定义一个事情或者功能. 等到需要的时候直接去用就好了. 那么这里定义的东西就是一个函数. 语法: def 函数名(形参): 函数体 函数名(实 ...

  7. linux 下 mysql安装和配置

    最近在学习R语言,看到R与数据库交互这一部分,就自己动手实践了一下,数据库选择的是mysql,主要记录下linux下怎么安装mysql. 网上的很多资料都有相关的文章,这里只是记录下自己安装过程中遇到 ...

  8. 改进意见的答复及bug重现

    各组对本组的互评链接如下 Thunder:http://www.cnblogs.com/vector121/p/7905300.html 王者荣耀交流协会:http://www.cnblogs.com ...

  9. 20172330 2017-2018-1 《Java程序设计》第十周学习总结

    20172330 2017-2018-1 <程序设计与数据结构>第十周学习总结 教材学习内容总结 本周的学习内容为集合 集合 对象具有定义良好的接口,从而成为一种实现集合的完善体制. 动态 ...

  10. 201621123037 《Java程序设计》第3周学习总结

    #Week03-面向对象入门 1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识点组织起来.请使用工具画出本周学习到的知识点及知识点之间的联 ...