NOIP2017 题解

Posted AntiLeaf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017 题解相关的知识,希望对你有一定的参考价值。

QAQ……由于没报上名并没能亲自去,自己切一切题聊以慰藉吧……
可能等到省选的时候我就没有能力再不看题解自己切省选题了……辣鸡HZ毁我青春

D1T1 小凯的疑惑

地球人都会做,懒得写题解了……

D1T2 时间复杂度

分类讨论+递归就行了,没啥思维含量,略。

D1T3 逛公园

这题好劲啊……

D2T1 奶酪

\(O(n^2)\)暴力就行了,水题。

D2T2 宝藏

看到数据范围一眼\(O^*(3^n)\)状压DP,其中\(3^n\)来自枚举子集的子集。做法好像有很多,比如ryf的做法就比我快了10倍……日渐辣鸡的窝
定义\(f_{i,j,S}\)表示以\(i\)为根的子树,\(i\)在整棵树里的深度是\(j\),集合\(S\)中的所有点都在这棵子树中时这棵子树的最小总代价。
不难写出转移方程:
\[f_{i,j,S}=\min_{T\subsetneq S,k\in T}\{f_{k,j+1,T}+f_{i,j,S-T}+j\times w_{i,k}\}\]
(这个转移方程是在枚举与\(i\)相邻的一个点\(k\)并把以\(k\)为根的子树分出去)
然而你发现这个DP是\(O(n^3 3^n)\)的,并不能跑过去。
考虑优化转移,如果把转移方程中与\(S\)无关的部分拿出来并定义一个辅助数组
\[g_{i,j,T}=\min_{k\in T}\{f_{k,j+1,T}+j\times w_{i,k}\}\]
的话,我们就可以把状态转移方程改写成
\[f_{i,j,S}=\min_{T\subsetneq S}\{f_{i,j,S-T}+g_{i,j,T}\}\]
这样就可以把复杂度降到\(O(n^2 3^n)\)了,由于常数不大,并不需要卡常。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define bit(x) (1<<((x)-1))
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,w[15][15],f[15][15][(1<<12)+1],g[15][15][(1<<12)+1];
int main(){
    scanf("%d%d",&n,&m);
    memset(w,63,sizeof(w));
    memset(f,63,sizeof(f));
    memset(g,63,sizeof(g));
    while(m--){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        w[x][y]=w[y][x]=min(w[x][y],z);
    }
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j][bit(i)]=0;
    for(int j=n-1;j;j--)for(int s=0;s<(1<<n);s++)for(int i=1;i<=n;i++){
        for(int k=1;k<=n;k++)if((bit(k)|s)==s&&w[i][k]<INF)
            g[i][j][s]=min(g[i][j][s],f[k][j+1][s]+w[i][k]*j);
        for(int t=s&(s-1);;(--t)&=s){
            f[i][j][s]=min(f[i][j][s],f[i][j][s^t]+g[i][j][t]);
            if(!t)break;
        }
    }
    int ans=INF;
    for(int i=1;i<=n;i++)ans=min(ans,f[i][1][(1<<n)-1]);
    printf("%d",ans);
    return 0;
}

D2T3 列队

这题真是劲啊……比往年的数据结构NB到不知哪儿去了……

以上是关于NOIP2017 题解的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2017题解

NOIP2017 题解

NOIP2017Day2题解

qboi冲刺NOIP2017复赛试题4 全套题目+题解+程序

NOIP2017总结 & 题解

NOIP2017-普及组复赛第2题 题解