暑期集训周报三

题解

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);
    }
}

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值