假期总结3(杂论)

1.约瑟夫环问题

https://www.luogu.com.cn/problem/P1996

https://www.luogu.com.cn/problem/P8671

解题思路

1.朴实无华的暴力模拟

#include<iostream>
using namespace std;
int a[1000100];

signed main()
{
	int n, k; cin >> n >> k;
	for (int i = 1; i <= n; i++)a[i] = 1;

	int cnt = 1;
	int flag = 0;
	int i = 1;

	while (1)
	{
		if (flag == n - 1)break;

		if (a[i] == 0)i++;
		else {
			if (cnt == k)
			{
				a[i] = 0;
				cnt = 1;
				flag++;	i++;
			}
			else
			{
				cnt++;
				i++;
			}
		}
		if (i == n + 1)i = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		if (a[i])cout << i;
	}
	return 0;
}

但是显然,有大概率超时的风险(我就超时了)

2.循环链表法(我暂时不会,跳过

3.递归法

#include<iostream>
using namespace std;
 
int Josephus Circle(int N,int M,int i)
{
    if(i==1)
    {
    	return (M-1+N)%N;
	}
    return (Josephus Circle(N-1,M,i-1)+M)%N;
}
 
 
int main()
{
	int N,M;
	cin>>N>>M;   //10   3
	for(int i=1;i<=N;i++)
		cout<<Josephus Circle(N,M,i)<<" ";
	return 0;
} 

对递归法,我的理解是:第N-1个人时第K-1个出局的人即为第N个人时第K个出局的人,于是一直往前推最终可以得出第?个人时第一个出局的人的编号,不断回溯最终得到所求值。其中得出结论的方法时:当每G掉一人时,对剩下的人重新编号,继续求下一个出局的人,可推得结论

4.递推公式

F(1)=0

F(n)=(F(n−1)+k)modn

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

int main()
{
    int n,k,s = 0;
    cin >> n >> k;
    for(int i = 2;i <= n;i++)
    {
        s=(s+k)%i;
    }
    cout << s+1; //求得最终出队的编号
    return 0;
}

 2.约瑟夫

https://www.luogu.com.cn/problem/P1145

#include<iostream>
using namespace std;
signed main()
{
	int k; cin >> k;
	int m = k; bool st = 0;
	while (!st)
	{
		m++;
		int point = 0;
		st = 1;
		for (int i = 0; i < k; i++)
		{
			point = (point - 1 + m) % (2*k-i);
			if (point < k)
			{
				st = 0;
				break;
			}
			if (i == k - 1)break;
		}
	}
	cout << m << endl;;
}

思路

与约瑟夫环基本完全相同,但是不同的出题形式,首先给出k,那么总人数就是2*k,由于不能选到好人,所以m的值最小也是k+1,从m=k+1开始,设初始位置为0,依次判断第一个出列的编号,第二个出列的编号.....第k个出列的编号,由于N个人时,第K个出列的==第N-1个人时,第K-1个出列的当满足条件时,输出m

2.二进制枚举

例:

https://www.acwing.com/problem/content/94/

这是一种神奇的暴力枚举的方法,其原理我个人认为是状态压缩,把每个情况选与不选压缩为一个数,再使其每位按位与1,判断选于不选,来得出结果

核心步骤


    int n=5;//5个小球
    for(int i=0; i<(1<<n); i++) //从0~2^5-1个状态
    {
        for(int j=0; j<n; j++) //遍历二进制的每一位
        {
            if(i&(1<<j))//判断二进制第j位是否存在
            {
                printf("%d ",j);//如果存在输出第j个元素
            }
        }
    }

https://vjudge.csgrandeur.cn/problem/AtCoder-arc114_a/origin

#include <bits/stdc++.h>
#define int long long
#define PI pair<int,int>
using namespace std;
const int maxm=2e6+5;
const int mod=1e9+7;

bool isprime(int x){
    for(int i=2;i*i<=x;i++){
        if(x%i==0)return 0;
    }
    return 1;
}
void solve(){
    vector<int>p;
    for(int i=2;i<=50;i++){
        if(isprime(i)){
            p.push_back(i);
        }
    }
    vector<int>a;
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        a.push_back(x);
    }
    int c=p.size();
    int ans=1e18;
    for(int i=0;i<(1<<c);i++){
        int temp=1;
        for(int j=0;j<c;j++){
            if(i>>j&1)temp*=p[j];
        }
        int ok=1;
        for(auto s:a){
            if(__gcd(temp,s)==1){
                ok=0;break;
            }
        }
        if(ok)ans=min(ans,temp);
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

另一道例题

https://vjudge.csgrandeur.cn/contest/567178#problem/C

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int c[20];
int a[20][20];
int cnt[20];
int n, m, x;

bool st()
{
	for (int i = 1; i <= m; i++)
	{
		if (cnt[i] < x)return false;
	}
	return true;
}

signed main()
{

	scanf("%d%d%d", &n, &m, &x);

	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &c[i]);
		for (int j = 1; j <= m; j++)scanf("%d", &a[i][j]);
	}

	int minn = 0x3f3f3f3f;

	for (int i = 1; i <= (1 << n); i++)
	{
		int time = 0;
		memset(cnt, 0, sizeof cnt);
		for (int j = 0; j < n; j++)
		{
			if (i & (1 << j))
			{
				time += c[j + 1];
				for (int k = 1; k <= m; k++)cnt[k] += a[j + 1][k];
			}
		}
		if (st())minn = min(minn, time);
	}
	if(minn==0x3f3f3f3f)minn=-1;
	cout << minn << endl;;
	return 0;
}

3.通.高精度乘法

1. 可计算两个超大数的乘积

#include<iostream>
#include<cstring>
using namespace std;
char a[100010], b[100010];
int a1[100010], b1[100010];
int c[100010];

signed main()
{
	cin >> a >> b;
	int lena = strlen(a);
	int lenb = strlen(b);
	for (int i = 1; i <= lena; i++)a1[i] = a[lena - i] - '0';
	for (int i = 1; i <= lenb; i++)b1[i] = b[lenb - i] - '0';

	for (int i = 1; i <= lena; i++)
		for (int j = 1; j <= lenb; j++)
		{
			c[i + j - 1] += a1[i] * b1[j];
		}

	int len = lena + lenb;

	for (int i = 1; i < len; i++)
	{
		if (c[i] > 9)
		{
			c[i + 1] += c[i] / 10;
			c[i] %= 10;
		}
	}
	while (c[len] == 0 && len > 1)len--;
	for (int i = len; i >= 1; i--)cout << c[i];
}

2.可计算一超大数和一整数的乘积

#include<iostream>
#include<vector>
using namespace std;
vector<int>A;
vector<int>sub(vector<int>&A,int b)
{
    vector<int>C;int t=0;
    for(int i=0;i<A.size()||t;i++)
    {
        if(i<A.size())t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}
signed main()
{
    string a;int b;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--)
    A.push_back(a[i]-'0');
    auto C=sub(A,b);
    for(int i=C.size()-1;i>=0;i--)
    cout<<C[i];
}

应用例:

1.阶乘之和

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(int b)
{
	vector<int> A,C;
	if(b==1)
	{
		C.push_back(1);
		return C;
	}
	int d=b;
	while(d)
	{
		A.push_back(d%10);
		d/=10;
	}
	while(--b)
	{
    	C.clear();
    	int t=0;
	for(int i=0;i<A.size()||t;i++)
	{
    	if(i<A.size())t+=A[i]*b;
    	C.push_back(t%10);
    	t/=10;
	}
    	A=C;
	}
	return C;
}
vector<int> add(vector<int>&A,vector<int>&B)
{
	vector<int> C;
	int t=0;
	for(int i=0;i<A.size()||i<B.size();i++)
	{
		if(i<A.size()) t+=A[i];
		if(i<B.size()) t+=B[i];
		C.push_back(t%10);
		t/=10;
	}
	if(t) C.push_back(1);
	return C;
}
int main()
{
	int b;
	cin>>b;
	auto C=mul(b);
	vector<int> E,F;
	E.push_back(0);
	while(1)
	{
		if(b==0)
		{
			break;
		}
		auto C=mul(b);
		F=add(C,E);
		E=F;
		b--;
	}
	for(int i=E.size()-1;i>=0;i--)
	{
		printf("%d",E[i]);
	}
}

汉诺塔问题

根据观察可知:将n个圆盘从A盘移到C盘至少需要(2^n)-步

递推公式为F【n】=2 * F【n-1】+1

 高精度+汉诺塔

双塔问题

#include<iostream>
#include<vector>
using namespace std;
vector<int>C;
vector<int>A;
int n;

signed main()
{
	 cin >> n;
	int k = n + 1;
	A.push_back(2);
	int ans=0;
	int t=0;
	while (--k)
	{
	    ans++;
		if (k == 1) t = -2;
		else  t = 0;
		C.clear();
		for (int i = 0; i < A.size() || t; i++)
		{
			if (i<A.size())t += A[i] * 2;
			C.push_back(t % 10);
			t /= 10;
		}
		while (C.size() > 1 && C.back() == 0) C.pop_back();
	//	A.clear();
		A = C;
	}
//	cout<<ans<<endl;
	for (int i = A.size() - 1; i >= 0; i--)cout << A[i];

	return 0;
}//观察可知:双塔问题的答案应为(2^(n+1))-2

 数楼梯

(坑题一道,我上手就是一个暴搜然后完美的TLE了)

#include<iostream>
using namespace std;
int n;
int ans;

void dfs(int u)
{
	if (u == n)
	{
		ans++;
		return;
	}
	if(u>n)return;

	dfs(u + 1);
	dfs(u + 2);
}

signed main()
{
	scanf("%d", &n);
	dfs(0);
	printf("%d\n", ans);
	return 0;
}

想了想发现另有玄机:

随着n不断增加,不同阶楼梯的上法为一个斐波那契数列(我果断再交,再次寄掉

#include <iostream>
using namespace std;
const int N = 5010;
long long f[N];
int main()
{
	int n;
	cin >> n;
	f[1] = 1;
	f[2] = 2;
	for(int i=3;i<=n;i++)
	{
		f[i] = f[i - 2] + f[i - 1] ;
	}
	cout << f[n] << endl;
}

最后只能高精度

#include<vector>
#include<iostream>
using namespace std;
vector<int>f[5010];
int n;
vector<int>add(vector<int>& a, vector<int>&b)
{
	int t = 0;
	vector<int>C;
	for (int i = 0; i < a.size() || i < b.size(); i++)
	{
		if (a.size())t += a[i];
		if (b.size())t += b[i];
		C.push_back(t % 10);
		t /= 10;
	}
	if (t)C.push_back(t);
	return C;
}
signed main()
{
	scanf("%d", &n);
	f[1].push_back(1);
	f[2].push_back(2);
	for (int i = 3; i <= n; i++)
	{
		f[i] = add(f[i - 1], f[i - 2]);
	}
	for (int i = f[n].size() - 1; i >= 0; i--)cout << f[n][i];
	return 0;
}//涉及到一个vector类型的数组的运用,之前没这么用过
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值