题解
SMU Summer 2024 Contest Round 8-CSDN博客
CSDN
积累题目
dp
1.花店橱窗
思路:用dp[i][j]表示第i个放在j位置上的最大价值,那么我们可以枚举i-1被放在了区间[i-1,j-1]的那个位置,找到最大价值部分,然后更新dp[i][j];最后要输出i放在了哪个位置,只需要倒推即可。
动态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][k]+v[i][j]);
代码:
#include<bits/stdc++.h>
using namespace std;
int v[105][105],dp[105][105];
int f,V;
void Printout(int maxn,int i,int j){//倒推第i个到底放在了哪个位置
if(i==0)
return;
for(int k=1;k<=V;k++)
if(dp[i-1][k]+v[i][j]==maxn){
Printout(dp[i-1][k],i-1,k);
break;
}
cout<<j<<" ";
}
int main(){
cin>>f>>V;
for(int i=1;i<=f;i++)
for(int j=1;j<=V;j++)
scanf("%d",&v[i][j]),dp[i][j]=-10000;//v数组当中有负值,dp中应该设最负最小值
for(int i=1;i<=f;i++){
for(int j=i;j<=V-(f-i);j++){
for(int k=i-1;k<j;k++){
dp[i][j]=max(dp[i][j],dp[i-1][k]+v[i][j]);
}
}
}
int maxn=-1;
for(int i=f;i<=V;i++)
maxn=max(maxn,dp[f][i]);
int ii,jj;
for(int i=f;i<=V;i++)
if(dp[f][i]==maxn){
ii=f;
jj=i;
break;
}
cout<<maxn<<endl;
Printout(maxn,ii,jj);
return 0;
}
未解决
B-国际旅行 Ⅱ_河南萌新联赛2024第(二)场:南阳理工学院 (nowcoder.com)
题解:
根据题意可以得知国际道路就是该图中的桥边,所以可以先通过 tarjan 缩点得到一颗树 ,树上的每个节点都代 表一个国家,每个节点的权值就是该国家的城市数量这些参数都可以通过 tarjan 求出.记缩完点后的总结点个数为n
解法1:tarjan 缩点后,我们考虑一种离线做法,将所有询问离线下来,按照询问给出的边权限制进行升序排 序,然后把缩完点后的图中所有边去掉,将当前权值不大于询问边权限制的边,依次加入图中,然后再查询u所在的联 通块第 小的权值,用并查集维护每个连通块,用线段树维护每个连通块权值第 小的点,每次合并都在线段树上直接 合并.总的复杂度为O (qlogn )
解法2:原图一共有 个结点的话那么缩点后每个国家拥有的城市数量的种类不超过 种,那么对于解法1种 使用线段树维护第 小的做法,我们可以使用 map 直接暴力维护联通块中第 k小的国家查找为O ( qn(1/2)) 暴力合并的复 杂度为 O( nlogn(1/2) ) 总的复杂度为O ( qn(1/2)+nlogn(1/2) )
解法3:在线做法,利用克鲁斯卡尔重构树的性质,(这里不过多展开讲解,有兴趣可以自行了解),对缩完点后的图 进行重构,在重构树上,每次从询问结点所在的叶子结点往上跳,找到满足条件的结点后,查询该结点所在子树包含的叶 子结点中权值第 大的叶子结点实际上就是查询区间第 大采用主席树维护,进行克鲁斯卡尔重构复杂度为O (nlogn),每次向上查找满足条件的结点采用二分或者倍增的方法复杂度为O ( logn),主席树每次查询的复杂度为 O( logn) 总 的复杂度为O((q+n)logn )
G-lxy的通风报信_河南萌新联赛2024第(二)场:南阳理工学院 (nowcoder.com)-----------图的遍历和最小生成树的构建
K-Magic Cube_河南萌新联赛2024第(二)场:南阳理工学院 (nowcoder.com)
代码:(为什么没解决还要插代码呢,因为好奇官方三百多行代码是怎么想的)
#include <cstring>
#include <fstream>
#include <iostream>
#include <map>
#include <queue>
#include <vector>
#define Buff ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef vector<char> vchar;
struct Plane // 面
{
char c[9];
Plane()
{
memset(c, 0, sizeof(c));
}
void set(char x) // 设置颜色,本题非必要
{
memset(c, x, sizeof(c));
}
bool check()const // 判断此面颜色是否相同
{
for(int i = 1; i < 9; i++)
{
if(c[i] != c[0])
return false;
}
return true;
}
void CRotate() // 此面顺时针旋转90度
{
char b = c[0];
c[0] = c[6];
c[6] = c[8];
c[8] = c[2];
c[2] = b;
b = c[1];
c[1] = c[3];
c[3] = c[7];
c[7] = c[5];
c[5] = b;
}
void CCRotate() // 此面逆时针旋转90度
{
char b = c[0];
c[0] = c[2];
c[2] = c[8];
c[8] = c[6];
c[6] = b;
b = c[1];
c[1] = c[5];
c[5] = c[7];
c[7] = c[3];
c[3] = b;
}
};
struct Cube // 魔方
{
Plane p[6];
Cube()
{
init();
}
void init() // 初始化颜色,本题非必要
{
p[0].set('R');
p[1].set('G');
p[2].set('Y');
p[3].set('O');
p[4].set('B');
p[5].set('W');
}
bool check()const // 判断是否已还原
{
for(int i = 0; i < 6; i++)
{
if(!p[i].check())
return false;
}
return true;
}
void operate(int opid) // 操作
{
if(opid == 1)
R1();
else if(opid == 2)
R2();
else if(opid == 3)
U1();
else if(opid == 4)
U2();
else if(opid == 5)
F1();
else if(opid == 6)
F2();
}
void roperate(int opid) // 逆操作
{
if(opid == 1)
R2();
else if(opid == 2)
R1();
else if(opid == 3)
U2();
else if(opid == 4)
U1();
else if(opid == 5)
F2();
else if(opid == 6)
F1();
}
void R1()
{
p[1].CRotate(); // 旋转面
int updateplane[4] = { 0,5,3,2 }; // 需要按顺序移位的相邻面
int updateid[4][3] = { { 2,5,8 }, // 每面需要移位的颜色下标
{ 6,3,0 },
{ 2,5,8 },
{ 2,5,8 } };
update(updateid, updateplane);
}
void R2()
{
p[1].CCRotate();
int updateplane[4] = { 0,2,3,5 };
int updateid[4][3] = { { 2,5,8 },
{ 2,5,8 },
{ 2,5,8 },
{ 6,3,0 } };
update(updateid, updateplane);
}
void U1()
{
p[2].CRotate();
int updateplane[4] = { 0,1,3,4 };
int updateid[4][3] = { { 0,1,2 },
{ 6,3,0 },
{ 8,7,6 },
{ 2,5,8 } };
update(updateid, updateplane);
}
void U2()
{
p[2].CCRotate();
int updateplane[4] = { 0,4,3,1 };
int updateid[4][3] = { { 0,1,2 },
{ 2,5,8 },
{ 8,7,6 },
{ 6,3,0 } };
update(updateid, updateplane);
}
void F1()
{
p[0].CRotate();
int updateplane[4] = { 1,2,4,5 };
int updateid[4][3] = { { 6,7,8 },
{ 6,7,8 },
{ 6,7,8 },
{ 6,7,8 } };
update(updateid, updateplane);
}
void F2()
{
p[0].CCRotate();
int updateplane[4] = { 1,5,4,2 };
int updateid[4][3] = { { 6,7,8 },
{ 6,7,8 },
{ 6,7,8 },
{ 6,7,8 } };
update(updateid, updateplane);
}
void update(int uid[4][3], int uplane[4]) // 循环移位相邻面颜色
{
char buffer[3] = { p[uplane[0]].c[uid[0][0]],
p[uplane[0]].c[uid[0][1]],
p[uplane[0]].c[uid[0][2]] };
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
p[uplane[i]].c[uid[i][j]] = p[uplane[i + 1]].c[uid[i + 1][j]];
}
p[uplane[3]].c[uid[3][0]] = buffer[0];
p[uplane[3]].c[uid[3][1]] = buffer[1];
p[uplane[3]].c[uid[3][2]] = buffer[2];
}
friend std::istream& operator>>(std::istream& os, Cube& cube) // 输入
{
std::string buffer;
for(int i = 0; i < 9; i += 3)
{
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[3].c[i + j] = buffer[0];
}
}
for(int i = 0; i < 9; i += 3)
{
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[4].c[i + j] = buffer[0];
}
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[2].c[i + j] = buffer[0];
}
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[1].c[i + j] = buffer[0];
}
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[5].c[i + j] = buffer[0];
}
}
for(int i = 0; i < 9; i += 3)
{
for(int j = 0; j < 3; j++)
{
os >> buffer;
cube.p[0].c[i + j] = buffer[0];
}
}
return os;
}
friend std::ostream& operator<<(std::ostream& os, const Cube& cube) // 输出,本题非必要
{
for(int i = 0; i < 9; i += 3)
{
os << " ";
for(int j = 0; j < 3; j++)
{
os << cube.p[3].c[i + j];
if(j < 2)os << ' ';
}
os << '\n';
}
for(int i = 0; i < 9; i += 3)
{
for(int j = 0; j < 3; j++)
{
os << cube.p[4].c[i + j] << ' ';
}
for(int j = 0; j < 3; j++)
{
os << cube.p[2].c[i + j] << ' ';
}
for(int j = 0; j < 3; j++)
{
os << cube.p[1].c[i + j] << ' ';
}
for(int j = 0; j < 3; j++)
{
os << cube.p[5].c[i + j];
if(j < 2)os << ' ';
}
os << '\n';
}
for(int i = 0; i < 9; i += 3)
{
os << " ";
for(int j = 0; j < 3; j++)
{
os << cube.p[0].c[i + j];
if(j < 2)os << ' ';
}
os << '\n';
}
return os;
}
};
/
void dfs(Cube& cube, vchar& way, bool& flag, size_t stop) // dfs深搜
{
if(way.size() >= stop)
return;
char ref = -1; // 上次操作的逆操作序号
if(!way.empty())
{
ref = *(way.rbegin());
if(ref & 1)
ref++;
else
ref--;
}
for(char i = 1; i <= 6; i++)
{
if(i == ref) // 剪枝
continue;
cube.operate(i);
way.emplace_back(i);
if(cube.check())
{
flag = true;
return;
}
dfs(cube, way, flag, stop);
if(flag)
return;
cube.roperate(i); // 回溯
way.pop_back();
}
}
vchar bfs(Cube icube) // bfs宽搜
{
using pcv = pair<Cube, vchar>; // 保存的魔方状态以及操作顺序
pcv ib;
ib.first = icube;
queue<pcv> q;
q.push(ib);
vchar ans;
while(!q.empty())
{
pcv b = q.front();
q.pop();
char ref = -1; // 上次操作的逆操作序号
if(!b.second.empty())
{
ref = *(b.second.rbegin());
if(ref & 1)
ref++;
else
ref--;
}
for(char i = 1; i <= 6; i++)
{
if(i == ref)
continue;
pcv x = b;
x.first.operate(i);
x.second.emplace_back(i);
if(x.first.check())
{
ans = x.second;
return ans;
}
q.push(x);
}
}
return ans;
}
void solve()
{
Cube cube;
cin >> cube;
if(cube.check())
{
cout << "0\n";
return;
}
vchar ans;
//ans = bfs(cube); // bfs调用
bool flag = false;
dfs(cube, ans, flag, 8); // dfs调用
cout << ans.size() << '\n';
vector<string> map_op{ "", "R1", "R2", "U1", "U2", "F1", "F2" }; // 操作映射
for(char i : ans)
{
if(i >= 1 && i <= 6)
cout << map_op[i] << '\n';
}
}
int main()
{
Buff;
int _N = 1;
//cin >> _N;
while(_N--)
solve();
return 0;
}
Permutation - SMUOJ——思维+dp
思路:
Hcode OnlineJudge
思路:
知识点
1.内存超限
删去#define int long long,或者将改变数组的数据类型都可能避免这个问题。
2.大组合数取模问题
(1)Lucas定理
m mod p 和n mod p都为小于p的数,前一部分可以继续用Lucas定理计算
ll getc(int n,int m,int p){
return f[n]*g[m]%p;
}
int lucas(ll n,ll m,int p){
if(m==0) return 1;
return lucas(n/p,m/p,p)*gest(n%p,m%p,p)%p;
}
3. 快速幂
int qm(int a,int k){
int res=1;
while(k){
if(k&1) res=res*a%p;
a=a*a%p;
k>>=1;
}
return res;
}
4.dfs 一定要return
void dfs(int pos,int w){
if(pos==n){
if(w==x) ans++;
return ;//这个,写吐了,我真的该吐了,没return ,所以一直访问到不存
//在的区间,浪费了我的大好青春,我真服了
}
for(int u:a[pos]){
if(w>x/u) continue;
dfs(pos+1,w*u);
}
}