首页 > 其他分享 >Living-Dream 系列笔记 第9期

Living-Dream 系列笔记 第9期

时间:2024-03-09 12:34:18浏览次数:29  
标签:Living int 31 笔记 dfs using include Dream sum

模拟赛掉大分(悲

T1

板子,不讲。

T2

首先很明显这题是个去重全排列。

和模板的区别就是加了一个 \(sum\) 参数记录目前已经放了几个苹果。

当 \(x=n+1\) 时若 \(sum=m\),则更新答案。

同时加入一个剪枝:若在搜索过程中 \(sum>m\),则直接 return 结束搜索。

#include<bits/stdc++.h>
using namespace std;

int t,n,m,ans;
int tmp[31];

void dfs(int x,int sum){
	if(x==n+1){
		if(sum==m) ans++;
		return;
	}
	if(sum>m) return;
	
	for(int i=tmp[x-1];i<=m;i++){
		tmp[x]=i;
		dfs(x+1,sum+i);
	}
}

int main(){
	cin>>t;
	while(t--){
		ans=0;
		cin>>m>>n;
		dfs(1,0);
		cout<<ans<<'\n';
	} 
	return 0;
}

如果你高兴还可以用 dp 做

T3

赛时没想出来,就打了个表跑路,没想到拿了 \(30pts\) /xia

将棋盘看成一个 \(25\) 格的数组,这个题就可以演变为填格子的搜索了。

在 \(\text{DFS}\) 函数中传入两个参数:\(x\) 和 \(tot\),分别记录当前已经填的格子数以及剩下的格子数,初始分别为 \(1,n\)。

若 \(tot=0\),则计算当前矩阵中的五连子个数,存入 \(vis\) 数组中。

否则,循环 \(x \sim 25\),对于每个枚举到的 \(i\),计算出对应的 \(x,y\) 坐标,将此坐标标记为已访问,并且调用 \(\text{dfs}(i+1,tot-1)\) 即可。注意回溯。

#include<bits/stdc++.h>
using namespace std;

int n,ans;
bool vis[31],v[31][31];

void check(){
    int sum=0;
    for(int i=1;i<=5;i++){
        for(int j=1;j<=5;j++){
            if(!v[i][j]) break;
            if(j==5) sum++;
        }
        for(int j=1;j<=5;j++){
            if(!v[j][i]) break;
            if(j==5) sum++;
        }
    }
    for(int i=1;i<=5;i++){
        if(!v[i][i]) break;
        if(i==5) sum++;
    }
    for(int i=1;i<=5;i++){
        if(!v[i][6-i]) break;
        if(i==5) sum++;
    }
    vis[sum]=1;
}

void dfs(int x,int tot){
	//cout<<x<<'\n';
    if(!tot){ check(); return; }
    if(25-x+1<tot) return;
    for(int i=x;i<=25;i++){
        int xx=(i-1)/5+1,yy=(i-1)%5+1;
        v[xx][yy]=1;
        dfs(i+1,tot-1);
        v[xx][yy]=0;
    }
}

int main(){
    cin>>n;
    dfs(1,n);
    for(int i=1;i<=12;i++)
        if(vis[i]) ans+=i;
    cout<<ans;
    return 0;
}

T4

这题的 \(\text{DFS}\) 做法很好写。将每个公司看作一个格子,分配的机器看作填入的数字,然后按常规的填格子写法即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,maxx;
int ans[31],tmp[31];
int a[31][31];

void dfs(int x,int sum,int tot){
	if(x==n+1){
		if(tot<=m){
			if(sum>maxx){
				maxx=sum;
				for(int i=1;i<=n;i++) ans[i]=tmp[i];
			}
		}
		return;
	}
	
	for(int i=1;i<=m;i++){
		tmp[x]=i;
		dfs(x+1,sum+a[x][i],tot+i);
	}
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	dfs(1,0,0);
	cout<<maxx<<'\n';
	for(int i=1;i<=n;i++) cout<<i<<' '<<ans[i]<<'\n';
	return 0;
}

无奈只有 \(10pts\)(为什么会 WA 两个点啊 \(qwq\))。

于是我们考虑万能的 \(dp\)。

我们令 \(f_{i,j}\) 表示前 \(i\) 个公司分配 \(j\) 个机器的利润总和。

因为分配给子公司的机器数只能为 \(0 \sim j\),则我们在此区间内枚举一个整数 \(k\) 表示给第 \(i\) 个公司分配的机器数,由此得到状态转移方程:

\[f_{i,j}=\max(f_{i,j},f_{i-1,j-k}+a_{i,k}) \]

同时,题目要求我们记录分配方案,因此建令 \(p_{i,j,h}\) 表示在前 \(i\) 个公司分配 \(j\) 个机器的最优方案中第 \(h\) 各公司分配到的机器数。

\(p\) 可以在状态转移的时进行更新。更新方程:

\[p_{i,j,h}= \begin{cases} 1 \le i <n \ , \ p_{i-1,j-k,h}\\ i = n \ , \ k \end{cases} \]

#include<bits/stdc++.h>
using namespace std;

int n,m;
int a[31][31],f[31][31],p[31][31][31];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<=j;k++){
                if(f[i][j]<f[i-1][k]+a[i][j-k]){
                    f[i][j]=f[i-1][k]+a[i][j-k];
                    for(int h=1;h<i;h++)
                        p[i][j][h]=p[i-1][k][h];
                    p[i][j][i]=j-k;
                }
            }
        }
    }
    
    cout<<f[n][m]<<'\n';
    for(int h=1;h<=n;h++) 
        cout<<h<<' '<<p[n][m][h]<<'\n';
    return 0;
}

哈哈没想到吧只有 90 分

由于题目再一次提出了毒瘤的要求:字典序最小。

因此我们考虑枚举整数 \(k\) 表示不给第 \(i\) 个公司的机器数。

所以转移方程就变成了这样:

\[f_{i,j}=\max(f_{i,j},f_{i-1,k}+a_{i,j-k}) \]

更新方程变成了这样:

\[p_{i,j,h}= \begin{cases} 1 \le i <n \ , \ p_{i-1,k,h}\\ i = n \ , \ k \end{cases} \]

愉快 \(\mathcal{AC}\) !

#include<bits/stdc++.h>
using namespace std;

int n,m;
int a[31][31],f[31][31],p[31][31][31];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<=j;k++){
                if(f[i][j]<f[i-1][k]+a[i][j-k]){
                    f[i][j]=f[i-1][k]+a[i][j-k];
                    for(int h=1;h<i;h++)
                        p[i][j][h]=p[i-1][k][h];
                    p[i][j][i]=j-k;
                }
            }
        }
    }
    
    cout<<f[n][m]<<'\n';
    for(int h=1;h<=n;h++) 
        cout<<h<<' '<<p[n][m][h]<<'\n';
    return 0;
}

标签:Living,int,31,笔记,dfs,using,include,Dream,sum
From: https://www.cnblogs.com/XOF-0-0/p/18062518

相关文章

  • Living-Dream 系列笔记 第6期
    模拟赛。寄。T1对于每次询问,二分查找数组中对应值的原下标即可,因此需要用结构体存储原始数据和原始下标。这当然是比较麻烦的做法。另一种做法则是开一个map替代桶来存储数组中每个元素的下标,对于每个询问输出即可。另外值得注意的是,本题默认询问之间相互独立。时间复杂度......
  • Living-Dream 系列笔记 第7期
    本期主要讲解深度优先搜索\(\text{DFS}\)。知识点种类:全排列。可以想象为填格子。去重全排列,即组合。时间复杂度均为\(O(n!)\)。\(\text{DFS}\)题的特征:求方案总数/最值。数据范围极小(一般\(n\le20\))。无法直接暴力枚举(因为循环嵌套层数不确定)。......
  • Living-Dream 系列笔记 第4期
    本期主要讲解二分答案。知识点使用场景:最小值最大化,或最大值最小化。在限制条件下找最值。与二分查找的区别:L、R均为答案,而非下标。输出:最大化输出L,反之输出R。例题T1二分\(M\)的值,边界为\(L=-1,R=\max{\{a_i\}}\)。每次枚举到一个\(mid\)就对于每个......
  • Living-Dream 系列笔记 第5期
    本期主要讲解二分答案的进阶。例题T1二分需要的秒数,在check函数中对于每件衣服,若其在\(x\)秒内无法自然晒干,则使用烘干机,并令\(sum\)加上使用烘干机的秒数,最后判断\(sum\)是否\(\lex\)即可。\(Trick\):二分边界需要按数据范围尽可能开大,不能开小了。#include<bits/......
  • 挑选一款适合自己的笔记本电脑的步骤
    你知道如何挑选一款适合自己的笔记本电脑吗?笔记本电脑有许多品牌和型号,因此挑选出合适的笔记本电脑是一项艰巨的任务。为了能够找到最适合你的笔记本电脑,你需要考虑一系列因素。需要先了解几个常见问题:·从什么渠道购买笔记本·笔记本电脑品牌都有哪些·购买笔记本首先弄清......
  • 3/9 训练笔记
    P5268[SNOI2017]一个简单的询问题解不妨把每个区间表示成\(|V|\)维向量\(b\)的形式,其中\(b[i]\)为在区间\([l,r]\)中,\(i\)出现的次数。然后我们发现要求的实际上是\(a\cdotb\)。拆一下(这里用\(g(i)\)表示\([1,i]\)的向量):\(a\cdotb=[l_1,r_1]\cdot[l_......
  • 课堂笔记2
    define_CRT_SECURE_NO_WARNINGSinclude<stdio.h>//////冒泡排序该方法只能进行整数的排序//voidBubbleSort(intarr[],intsz)//{//inti=0;//intj=0;//for(i=0;i<sz-1;i++)//{//for(j=0;j<sz-1-i;j++)......
  • 王道计算机网络截图笔记
    目录第一章概述1.计算机网络概览1.1网络与计算机网络1.2计算机网络的功能1.3计算机网络的组成1.3.1组成部分1.3.2工作方式1.3.3功能组成1.4计算机网络的分类1.5小结2.计算机网络的标准化工作2.1标准的分类2.2RFC2.3标准化工作的相关组织2.4小结3.计算机网络性能指标3......
  • 2024.3.9 笔记
    2024.3.9笔记P1948题目大意为在无向图上求出从\(1\)到\(N\)的路径,使路径上第\(k+1\)大的边权尽量少。第一种是DP用\(f[i][j]\)表示从\(1\)到点\(i\),用了\(j\)条免费线的最小花费。对于一条\(u->v\)的边\(e\)有转移:不用免费线\(f_{v,k}=min(f_{v,k},max......
  • MYSQl学习笔记19: 外键约束
    外键约束用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性具有外键的表(emp)称为子表外键关联的表(dept)称为父表外键约束创建表时添加createtable表名(字段名数据类型,[constrain][外键名称]foreignkey(外键字段名)references主表(主表......