2022.3.21

蓝书

AcWing 193. 算乘方的牛

这里估计函数可以设计为当前的最大的数到目标数还需要乘多少次2。对于题目要求我们可以有8种操作方式,1.将a+b放在a的位置。2.将a+b放在b的位置再交换。3.a自乘。4.a自乘放在b的位置再交换。5.b自乘放在a的位置。6.b自乘再与a交换。7.a除b放在a的位置。8.a除b再与b交换。这样做还是不够会超时,有个强剪枝是结果n肯定是a和b经过多次相乘得来的,因此n一定含有a,b的最大公约数。由此来判断是否合法。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+10,INF=1e8;
int n;
int gcd(int a,int b)
{
    return b ? gcd(b, a % b) : a;
}
bool dfs(int dep,int cnt,int a,int b)
{
    if(dep==cnt)
    {
        if(a==n)
            return 1;
        return 0;
    }
    if(a<<(dep-cnt)<n)
        return 0;
    if(n%gcd(a,b)!=0)
         return 0;
    
    int x = a + b, y = b;
    if(b&&dfs(dep,cnt+1,x,y))
        return 1;
    x = a + b, y = a;
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = a + a, y = b;
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = a + a, y = a;
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = b + b, y = a;
    if(x<y)
        swap(x, y);
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = b + b, y = b;
    if(x<y)
        swap(x, y);
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = a - b, y = a;
    if(x<y)
        swap(x, y);
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    x = a - b, y = b;
    if(x<y)
        swap(x, y);
    if(y&&dfs(dep,cnt+1,x,y))
        return 1;
    
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    int dep = 0;
    while(!dfs(dep,0,1,0))
    {
        dep++;
    }
    cout << dep;
    return 0;
}

AcWing 182. 破坏正方形

首先需要将正方形的每根火柴表示出来,然后迭代加深枚举所有的正方形,判断该正方形的边是否是完整的,如果是完整的说明我们需要破坏它,枚举破坏的边,继续向下搜索,这里的估计函数是找到如果找到一个完整的正方形,我们把它所有的边都破坏并只记一次操作,这样的操作次数肯定是小于等于实际的操作次数的,不断搜索,直到没有完整的正方形时返回。
对于如何表示火柴的编号,把火柴分成横着的和竖着的两部分。对于横着的火柴,(r,c)为(r,0)+c得来,对于坐标为(r,0)的点,可以构成一个首项为1,公差为2n+1(横着的n根,竖着的n+1根,一共跨了2n+1根)的等差数列,那么(r,0)的编号为1+(2n+1)r。(r,c)的编号为1+(2n+1)+c。接着考虑竖着的火柴,发现对于(r,c)的竖着的火柴其坐标为与其同为(r,c)的横着的火柴的编号加上n,于是对于一个边长为len的位于边长为n的大正方形里的子正方形:从上边横着的从左往右开始数,设其坐标为(r,c+i) 其编号为1+(2n+1)r+c+i;从下边横着的从左往右开始数,其坐标为(r+len,c+i),其编号为:1+(2n+1)(r+len)+c+i。从左边从上往下数的竖着的火柴:其坐标为(r+i,c),其编号为1+(2n+1)(r+i)+c+n。从右边从上往下数的竖着的火柴:其坐标为(r+i,c+len),其编号为1+(2n+1)(r+i)+c+len+n。看了代码,给木棍实现编号困难。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=60+10,INF=1e8;
int n, m,vis[N];
vector<int> a[N];
bool check(int x)
{
    for (int i = 0; i < a[x].size();i++)
    {
        if(vis[a[x][i]])
            return 0;
    }
    return 1;
}
int dis()
{
    int ans = 0, back[N];
    memcpy(back, vis, sizeof vis);
    for (int i = 0; i < m;i++)
    {
        if(check(i))
        {
            ans++;
            for (int j = 0; j < a[i].size();j++)
            {
                vis[a[i][j]] = 1;
            }
        }
    }
    memcpy(vis, back, sizeof vis);
    return ans;
}
bool dfs(int dep,int cnt)
{
    if(cnt+dis()>dep)
        return 0;
    for (int i = 0; i < m;i++)
    {
        if(check(i))
        {
            for (int j = 0; j < a[i].size();j++)
            {
                vis[a[i][j]] = 1;
                if(dfs(dep,cnt+1))
                    return 1;
                vis[a[i][j]] = 0;
            }
            return 0;
        }
    }
    return 1;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n;
        memset(vis, 0, sizeof vis);
        m = 0;
        for (int len = 1; len <= n;len++)
            for (int i = 1; i + len - 1 <= n;i++)
                for (int j = 1; j + len - 1 <= n;j++)
                {
                    int d = 2 * n + 1;
                    for (int k = 0; k < len;k++)
                    {
                        a[m].push_back(k + (i - 1) * d + j);
                        a[m].push_back(k + (i - 1 + len) * d + j);
                        a[m].push_back(n + 1 + (i - 1) * d + j - 1 + k * d);
                        a[m].push_back(n + 1 + (i - 1) * d + j - 1 + k * d + len);
                    }
                    m++;
                }
        int k;
        cin >> k;
        while(k--)
        {
            int x;
            cin >> x;
            vis[x] = 1;
        }
        int dep = 0;
        while(!dfs(dep, 0)) dep++;
        cout << dep << '\n';
        for (int i = 0; i < m;i++)
            a[i].clear();
    }
    return 0;
}

AcWing 192. 立体推箱子2

是立体推箱子的另一个版本,这个版本少了很多限制,在一张无限大的地图上可以随便走,其他规则和立体推箱子一样,有三种状态,分别是立着,竖着躺着,横着躺着。我们可以想之前一样枚举三种状态下箱子的x轴,y轴,sta的变化,然后bfs寻找最短的操作,但x和y的范围到1e9,显然开不了这么大的数组,因为是无限大的平面,并且x,y可能走着走着变成负数,可以加上偏移量100。我们可以选定一个终点(100,100)为中心的200x200的地图,如果x,y超过了200,我们可以把问题转化为从x,y到地图边界距离加上地图里打好的表,即地图里到终点的距离之和。考虑到是最短操作,我们可以用2次操作而走3步,因此我们可以计算x,y坐标到边界的距离,再将距离/3再*2,因为除法向下取整我们可以+1,得到我们到边界需要的操作步数。同时还需要考虑输入时箱子的状态,如果箱子是立着的还好说可以直接计算,如果不是立的就得把箱子先变成立着的再进行计算。枚举十种状态,如下图
红色为原先位置,每一个蓝色格子都为一种立着的情况,只有10种,因为对于任一第11种,可以从这10种中多走一步得到。

类似H,V也只有10种

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=200+10,INF=1e10;
struct rec
{
	int x, y, sta;
};
queue<rec> que;
int dis[N][N][5], x, y,ans=INF;
int nex[3][4]={{0,0,-2,1},{0,0,-1,1},{0,0,-1,2}};
int ney[3][4]={{-2,1,0,0},{-1,2,0,0},{-1,1,0,0}};
int nes[3][4]={{1,1,2,2},{0,0,1,1},{2,2,0,0}};
bool check(int x,int y)
{
    return x > 0 && x <= 200 && y > 0 && y <= 200;
}
void bfs()
{
    queue<rec> que;
    que.push({0, 0, 0});
    dis[0][0][0] = 0;
    while(que.size())
    {
	auto t = que.front();
	que.pop();
	for (int i = 0; i < 4;i++)
	{
	    rec next;
	    next.x = t.x + nex[t.sta][i];
	    next.y = t.y + ney[t.sta][i];
  	    next.sta = nes[t.sta][i];
	    if(check(next.x,next.y))
	        {
		    if(dis[next.x][next.y][next.sta]==INF)
		        {
			    dis[next.x][next.y][next.sta] = dis[t.x][t.y][t.sta] + 1;
			    que.push(next);
			}
		}
	}
    }
}
int cal(int x,int y)
{
    int sum = 0;
    if(x>200)
    {
        int step = (x - 200) / 3 + 1;
        sum += step * 2;
        x -= step * 3;
    }
    if(y>200)
    {
        int step = (y - 200) / 3 + 1;
        sum += step * 2;
        y -= step * 3;
    }
    return dis[x][y][0] + sum;
}
int main()
{
    char s[5] = {0};
    for (int i = 0; i <N ;i++)
	for (int j = 0; j < N;j++)
	    for (int k = 0; k < 3;k++)
		dis[i][j][k] = INF;
    bfs();
    while(cin>>s)
	{
        ans = INF;
        cin >> x >> y;
        x += 100, y += 100;
        if(s[0]=='U')
            ans = cal(x, y);
        else if(s[0]=='H')
        {
            for(int i=-2;i<=2;i++)
            {
                ans=min(ans,cal(x-i,y-1)+1+abs(i));
                ans=min(ans,cal(x-i,y+2)+1+abs(i));
            }
        }
        else if(s[0]=='V')
        {
            for(int i=-2;i<=2;i++)
            {
                ans=min(ans,cal(x-1,y+i)+1+abs(i));
                ans=min(ans,cal(x+2,y+i)+1+abs(i));
            }	
        }
        cout << ans << '\n';
    }
	return 0;
}

posted @ 2022-03-21 14:44  menitrust  阅读(19)  评论(0编辑  收藏  举报