第四次模拟赛订正题解

这次考动态规划专练;

下一次考数据结构专练???

坦白讲:这东西我还是不会;

现在发现好像是一个叫做树的最大独立集的;

有个暴力40分的做法,预处理所有的深度(层数),不是相邻层的累加,最后比较max,这个复杂度过不去,也只是贪心的思想,所以写完爆搜之后我还是去写了树形dp;

我们用dp[i][K]表示当前节点i选择(k==1)或不选择(k==0);而且比较巧妙地是,在统计方案树的是否,我们直接可以用当前k的表示该节点的方案,累加即可,好妙啊;

给出的题解:

#include<bits/stdc++.h>
using namespace std;
#define N 1000010
vector<int> G[N];
int n,u,v;

template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch==‘-‘)  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
    x*=f;
}

void dfs(int u,int fa) {
    for(int i=0;i<G[u].size();i++) {
        if(G[u][i]!=fa) {
            dfs(G[u][i],u);
        }
    }
}

int d[N][2]={0};
int dp(int u,int k,int fa) {
    d[u][k]=k;
    for(int i=0;i<G[u].size();i++) {
        if(G[u][i]!=fa) {
            if(k){//如果选,子节点不选
                d[u][k]+=dp(G[u][i],0,u);
            }
            else{//如果不选,选子节点或不选子节点
                d[u][k]+=max(dp(G[u][i],0,u),dp(G[u][i],1,u));
            }
        }
    }
    return d[u][k];
}

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    read(n);
    for(int i=1;i<n;i++) {
        read(u); read(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,-1);
    cout<<max(dp(1,0,-1),dp(1,1,-1));
    return 0;
}

第二题:

环形合并石子:区间dp;破环为链,复制一倍;

#include<bits/stdc++.h>
using namespace std;
#define N 1000
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch==‘-‘)  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-‘0‘;  ch=getchar();}
    x*=f;
}
int n,a[N],sum[N],h[N][N];
int main() {
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    read(n);
    memset(h,0xcf,sizeof(h));
    for(int i=1;i<=n;i++)
        read(a[i]),a[i+n]=a[i];
    for(int i=1;i<=n*2;i++)
        sum[i]=sum[i-1]+a[i],h[i][i]=0;
    for(int len=2;len<=n;len++)
        for(int l=1;l+len<=n*2+1;l++) {
            int r=l+len-1;
            for(int k=1;k<r;k++) {
                h[l][r]=max(h[l][r],h[l][k]+h[k+1][r]+sum[r]-sum[l-1]);
            }
        }
    int ans2=-(1<<30);
    for(int i=1;i<=n;i++) {
        ans2=max(ans2,h[i][i+n-1]);
    }
    cout<<ans2<<endl;
    return 0;
}

第三题:

看数据范围:

非常容易想到状压dp,本身就是一种暴力做法;

dp[i][j]表示当前i状态下,最后一位放的是j,感觉好套路啊这样的状态;

那么我们只需要判断当前状态下是否能放w,状态转移是累加方案即可;

给出的题解:

竟然考了状压dp;

#include<bits/stdc++.h>
using namespace std;
#define N 20
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch==‘-‘)  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-‘0‘;  ch=getchar();}
    x*=f;
}

long long n,k,a[N],f[1<<N][N];
long long ans;

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    read(n); read(k);
    for(int i=1;i<=n;++i)  {
        read(a[i]);
        f[1<<(i-1)][i]=1;//第i位放i的方案初始化1
    }
    for(int i=1;i<(1<<n);++i) {//枚举状态
        for(int j=1;j<=n;++j) {//当前放j
            for(int w=1;w<=n;++w) {//下一个放w
                if(i&(1<<(w-1))) continue;//如果当前状态下w位不能放w
                if(abs(a[j]-a[w])>k) {//满足题目要求的k可以放
                    f[i|(1<<(w-1))][w]+=f[i][j];//更新状态,累加答案
                }
            }
        }
    }
    for(int i=1;i<=n;++i)  {
        ans+=f[(1<<n)-1][i];
    }
    cout<<ans<<endl;
    return 0;
}

最后祝贺Chdy大佬不到一个小时AK;

原文地址:https://www.cnblogs.com/Tyouchie/p/11076536.html

时间: 2024-10-04 01:11:28

第四次模拟赛订正题解的相关文章

1015&amp;&amp;1017模拟赛订正题解

1015 反正这两次的模拟赛都不太会写吧 感觉越来越dl 今天初赛 真好 T1 反正题目给你excatalan 提醒你是卡特兰数了 m=0 的情况 你发现就是卡特兰 那么考虑 m为任意数字的 方法 显然 我们需要知道卡特兰数的证明方法 其实昨天探讨的时候 想了更多方法 有必要解决这样的问题 首先证明方法有折线法 由折线法 我们不妨引出 另外一种证明思想 卡特兰数对应的 问题模型都是 在第K次执行 操作2的时候 操作1都是至少执行了K次 那么我们用x轴表示 当前的操作次数 一共需要2n次 然后把操

10 01模拟赛订正

好吧,这是我第一次写模拟赛的订正,主要是有时间而且这次的题确实好... 第一题确实好,用的算法人人都会,就是看你能不能想到,我考只打了O(n^4)的暴力,最后还苦逼的MLE,爆零了... 暴力就不多说了...枚举两个点更新其他的点... 其实我考场上思考的是,能被标记的点都与其他的点有什么联系,可惜,除了模拟题目的做法,就不会了... 那让我们就认真地思考一发:我们设A(x1,x2),B(x2,c2),C(x3,c3)来更新D点,只有:有两个点横坐标相等,有两个点纵坐标相等,才可以更新出来一个新

2019-09-03【高一组】模拟赛 3 &amp;【高二组】模拟赛 2 题解

计算题 1 $$\begin{align}ans_x&=\sum_{i=1}^n (w_i-w_x)^2\\&=\sum_{i=1}^n(w_i^2+w_x^2-2w_iw_x)\\&=\sum_{i=1}^{n}w_{i}^2+nw_x^2-2\sum_{i=1}^n{w_i}w_x\end{align}$$ 先在读的时候处理出 $\begin{align}\sum_{i=1}^{n}w_{i}^2\end{align}$ 和 $\begin{align}\sum_{i=1}^n

计蒜课 八月模拟赛题解

看见机房有大佬上周写了上面的普及信心赛 于是我康了康 8月的提高组模拟赛 9月的还没开始qwq 真的 有点难 主要是我先打开了T2 我再次 对自己的数学产生了怀疑 我现在还是不会写T2 T1 又又又又都错题了 下次重建图 尽量写vector 都写 邻接表 变量差不多的容易搞混 我这个同学变又写错了 T1 :https://nanti.jisuanke.com/t/41086 题目大意就是 一个有向图 删一个点 把与他直接和间接 相连的点 删掉 然后 求删掉所有点的最小最大代价 : 为了避免这个环

【题解】PAT团体程序设计天梯赛 - 模拟赛

由于本人愚笨,最后一题实在无力AC,于是只有前14题的题解Orz 总的来说,这次模拟赛的题目不算难,前14题基本上一眼就有思路,但是某些题写起来确实不太容易,编码复杂度有点高~ L1-1 N个数求和 设计一个分数类,重载加法运算符,注意要约分,用欧几里得算法求个最大公约数即可. 1 #include <cstdio> 2 3 long long abs(long long x) 4 { 5 return x < 0 ? -x : x; 6 } 7 8 long long gcd(long

10-4国庆节第七场模拟赛题解

10-4 国庆节第七场模拟赛题解 T1工厂 (factory) 水 #include<iostream> #include<cstdio> #define int long long using namespace std; inline int read(){ int sum=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0

lzoi模拟赛题解

A题:签到题原题:mtoi 联赛 的A题定位:真.签到题(普及B题或者提高d1A题)考点:选手的基本数学能力思维难度:提高-代码难度:普及A题 题解:80%:暴力枚举100%:注意到(a xor b)<=(a+b),于是把所有的数异或起来即可. B题:送分题原题:[多省省队联测]d2A:皮配定位:一道联赛d1B题,考察了选手的基本功.送了选手70分.把70%的2种做法扩展可以得到正解考点:多种背包dp,计数思维难度:提高代码难度:提高+ 前面的几个数据可以暴力枚举解决.50%的数据:考虑dp.设

10月15日模拟赛题解

10月15日模拟赛题解 A 树 Description 给定一棵 \(n\) 个节点的树,每个节点有两个参数 \(a,~b\),对于每个节点,求子树中参数为 \(b\) 的所有节点的 \(a\) 之和 Limitations \(100\%\) \(1 \leq b \leq n \leq 10^5,~a \leq 1000\) \(60\%\) \(1 \leq b,n\leq 1000\) \(30\%\) \(1 \leq b, n \leq 10\) Solution 对于 \(30\%

题解 【重庆八中模拟赛】寻找代表元

[重庆八中模拟赛]寻找代表元 Description 八中一共有n个社团,分别用1到n编号.八中一共有m个人,分别用1到m编号.每个人可以参加一个或多个社团,也可以不参加任何社团.每个社团都需要选一个代表.我们希望更多的人能够成为代表.这里,每个人至多代表一个社团且每个社团至多有一个代表. Input 第一行输入两个数n和m.以下n行每行若干个数,这些数都是不超过m的正整数.其中第i行的数表示社团i的全部成员.每行用一个0结束. Output 输出最多的能够成为代表的人数. Sample Inp