AcWing打卡记录day 3

AcWing 96. 奇怪的汉诺塔
和一般的汉诺塔不同,这个有四个塔。
在三塔的时候,我们可以理解成,我们放n个圆盘的时候,是先将n-1个圆盘转移到B盘,然后将第n个转移到C盘,再将n-1个圆盘转移回来。
所以状态转移是

f[i]=f[i-1]*2+1;//整体移动两次(i-1)个圆盘  然后加上移动一次第n个圆盘。

当我们四塔的时候,和三塔类似。
因为三塔只有3个塔,在前面n-1个圆盘占据了一个塔之后,相当于只剩下两个塔给他转移了。
所以只能最后一个圆盘转移。
但是四塔的时候,将n-1个圆盘转移到一个塔之后,还剩下三个塔给他进行操作。
所以可以先转移前面一部分到不是D盘到一个盘,然后再转移后面一部分到D盘,
再将前面一部分转移到D盘。

// Problem: 奇怪的汉诺塔
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/98/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Date:2021-10-07 09:03:26
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define endl '\n'
const int inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
int n, m, T;
int flag;

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int f[20];
	int a[20];
	a[1]=1;
	for(int i=2;i<=12;i++){
		a[i]=a[i-1]*2+1;//记录的是  三塔状态下转移i个的最小步数。
	}
	for(int i=1;i<=12;i++){
		f[i]=inf;
	}
	f[1]=1,f[0]=0;
	for(int i=2;i<=12;i++){
		for(int j=0;j<i;j++){
			f[i]=min(f[i],f[j]*2+a[i-j]);
			//f[j]表示在四塔时先把j个转移到不是D塔的一个塔
			//a[i-j]表示,因为D塔都是比他小的圆盘,所以只有三个塔能用
			//则表示三塔时,吧剩下的i-j个转移到D塔
			//再f[j]表示四塔时转移到D他的最短步数。
			//因为吧i拆分成不同的部分得到=的值不一样 所以要遍历取最小值。
		}
	}
	for(int i=1;i<=12;i++){
		cout<<f[i]<<endl;
	}
	return 0;
}

310. 启示录
一开始以为就是简单的数位dp,想着直接把所有情况找出来,后来才反应过来,记录不了。
只能一次一次的二分查找。然后因为记忆化搜索,每次情况都查找完后,每次查找接近O(logn)所以不会超时。

// Problem: 启示录
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/312/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Date:2021-10-07 09:52:23
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define endl '\n'
const ll inf = 500000000000;
const int MAXN = 1e5 + 10;
int n, m, T;
int flag;

int t[MAXN];
int a[30];
ll dp[30][10][2]; //第二维表示之前有几个连续的3

ll dfs(int pos, int num, bool limit /*数位上界变量*/, int flag) //不是每个题都要判断前导零
{
	// flag 表示这个数是否已经满足条件
	if (num >= 3)
		flag = 1;
	if (pos == -1)
		return flag;
	if (!limit && dp[pos][num][flag] != -1)
		return dp[pos][num][flag];
	int up = limit ? a[pos] : 9;
	ll ans = 0;
	for (int i = 0; i <= up; i++) {
		if (i == 6)
			ans += dfs(pos - 1, num + 1, limit && i == a[pos], flag);
		else
			ans += dfs(pos - 1, 0, limit && i == a[pos], flag);
	}
	if (!limit)
		dp[pos][num][flag] = ans;
	return ans;
}

ll solve(ll x)
{
	int pos = 0;
	while (x) //把数位都分解出来
	{
		a[pos++] = x % 10;
		x /= 10;
	}
	return dfs(pos - 1, 0, true, 0); //刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	memset(dp, -1, sizeof(dp));
	cin >> T;
	while (T--) {
		cin >> n;
		ll l = 0, r = inf, mid;
		while (l < r) {
			mid = (l + r + 1) >> 1;
			if (solve(mid)<n) {
				l = mid;
			} else {
				r = mid - 1;
			}
		}
		cout << l+1 << endl;
	}
	return 0;
}

311. 月之谜
这个题目,一开始想是,数位dp,然后dfs下放的是,当前位数前面的位数数字的和,和这个数本身。
但是这样的话,就得相当于暴力搜索,肯定会超时。后来看题解,说是一开始就假设一下mod等于这个数所有位数上的数加起来的值sum,然后数位dp 得到这个数num对mod求余的结果,如果到最后summod并且num0 则表示这个数是魔鬼数。

// Problem: 月之谜
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/313/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
// Date:2021-10-07 10:58:44
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define endl '\n'
const int inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
int mod;
int n, m, T;
int flag;	
int a[33];
ll dp[20][200][200]; //不同题目状态不同

ll dfs(int pos, bool limit, int sum,int num) //不是每个题都要判断前导零
{
	if (pos == -1) {
		if (sum == mod && !num)
			return 1;
		return 0;
	}
	if (!limit && dp[pos][sum][num] != -1)
		return dp[pos][sum][num];
	int up = limit ? a[pos] : 9;
	ll ans = 0;	
	for (int i = 0; i <= up; i++) {
		ans += dfs(pos - 1, limit && i == a[pos], sum + i, (num * 10 + i) % mod);
	}
	
	return dp[pos][sum][num] = ans;
}

ll solve(ll x)
{
	int pos = 0;
	while (x) //把数位都分解出来
	{
		a[pos++] = x % 10;
		x /= 10;
	}
	ll ans = 0;
	for (mod = 1; mod <200; mod++) {
		memset(dp, -1, sizeof(dp));
		//因为每次mod都不一样  所以dp数组得初始化
		ans += dfs(pos - 1, true, 0, 0);
		// cout << ans << endl;
	}
	return ans;
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	ll l, r;
	cin >> l >> r;
	cout << (solve(r) - solve(l-1)) << endl;
	return 0;
}

97. 约数之和
看到的第一眼就是之前那个结论;
唯一分解定理即任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积N=P1a1P2a2P3a3…Pnan,这里P1<P2<P3…<Pn均为质数,其中指数ai是正整数。
然后因为是要求这些数的和,所以再根据约束和定理递归得到结果即可,

// Problem: 约数之和
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/99/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Date:2021-10-07 09:37:32
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define endl '\n'
const ll inf = 0x3f3f3f3f;
const ll MAXN = 1e5 + 10;
const ll mod = 9901;
ll n, T;
ll flag;

ll ksm(ll a, ll b)
{
	ll ans = 1;
	while (b) {
		if (b & 1)
			ans = (ans * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return ans;
}


ll primes[1000];
ll num[1000];
ll cnt=1;
void devide(ll n)//分解质因数
{
	for (ll i = 2; i <= n / i; i++) { //因为n一直在变小, 当n小于i的平方的时候,则表示后面已经没有满足的质数了
		if (n % i == 0) {
			primes[cnt]=i;
			while (n % i == 0) { //求出是这个质数的几次方
				num[cnt]++;
				n /= i;
			}
			cnt++;
		}
	}
	if (n > 1) { //如果还有剩下的。
		num[cnt]=1;
		primes[cnt++]=n;
	}
}

ll sum(ll a, ll b)
{
	if (b == 1)
		return 1;
	if(b % 2 == 0) {  
        return (ksm(a, b / 2) + 1) * sum(a, b / 2) % mod;
    }
    return (ksm(a,b - 1) + sum(a, b - 1)) % mod;
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	ll a, b;
	cin >> a >> b;
	if (a == 0) {
		cout << 0 << endl;
		return 0;
	}
	ll ans = 1;
	// cout<<1;
	devide(a);
	// cout<<1;
	for (ll i=1;i<cnt;i++) {
		ll p=primes[i],k=num[i]*b;
		// cout<<p<<" "<<k<<endl;
		ans = (ans * sum(p,k +1)) % mod;
		// ans = (ll)ans * sum(p, k + 1) % mod;
		//一开始想直接把sum里面的判断换一下,但是确实0次方也是需要考虑的 所以+1即可处理
		//因为是 b次方 而sum求的是到b-1项  所以要+1才行。
	}

	cout << ans << endl;
	return 0;
}

98.分形之城
看半天题目没看懂题意,直接看的题解。
题意就是那张图,让你看着图片找出他的规律。
找出规律后,每次转移后 再将原点转换即可。

// Problem: 分形之城
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/100/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Date:2021-10-07 20:28:44
// 算法:
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define endl '\n'
const int inf=0x3f3f3f3f;
const int MAXN=1e5+10;
int n,m,T;
int flag; 
typedef pair<ll,ll> pll ;

pll digui(ll n,ll m){
	if(n==0){
		// cout<<"xcxz";
		return {0,0};
	}
	ll len=1ll<<(n-1);//表示转移其与原点需要转移的距离。
	ll cnt=1ll<<(2*n-2);//表示的是 将这个等级的数分成四份 每一份是多少城市
	pll  pos=digui(n-1,m%cnt);//表示在上个等级的城市中是该部分的编号。
	ll pact=m/cnt;//表示在坐标轴哪个界限
	ll x=pos.first,y=pos.second;
	// cout<<x<<" "<<y<<endl;
	if(pact == 0)  return {-y-len,-x+len};
    if(pact == 1)  return {x + len,y+len};
    if(pact == 2)  return {x + len,y - len};
    return {y-len,x - len};
}

int main() 
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>T;
	while(T--){
		ll a,b;
		cin>>n>>a>>b;
		pll da=digui(n,a-1);//之所以减1 是为了便于第35行 
		// m%cnt表示在上个等级的城市区域的编号,即 0~15分别表示1~16
		pll db=digui(n,b-1);
		double x=da.first-db.first;
		double y=da.second-db.second;
		// cout<<x<<" "<<y<<endl;
		printf("%.0lf\n",sqrt(x*x+y*y)*5);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值