费解的机关
题目:https://www.acwing.com/problem/content/97/
思路:
其实当我看到这道题的时候,真的是二丈和尚摸不到脑袋,完全想不出思路来。然后看了一下答案,我也是更加迷惑,总感觉无法理解答案的思路,为什么只要固定第一行就可以了呢?然后再仔细想了一下,我才想通。
每个点作为中心点被点的次数不能超过一次,你想如果某个点作为中心点被点了两次,那不相当于没点吗?还白白浪费一次 。所以这个问题其实可以看作一个“排列”问题,就是5*5的点中有哪些作为中心点被点一次后能让灯全部都亮起来,而且点的次数最短。所以再看看书上给的答案,固定第一行:就相当于给定了第一行中点与不点的情况。进而导致,如果在固定第一行之后,如果第一行还有0的话,那么只能通过 点第二行的灯才能将第一行的0变成1,如果第一行的第k号灯已经是1了,那么第二行的第k号灯就没必要点了,这样第二行点的情况也被固定住了,进而是第3、4、5……。最后通过判断最后一行是否全为1判断此种方法是否可行。
#include<iostream>
using namespace std;
const int maxn=10;
int data1[maxn][maxn],temp[maxn][maxn],ans=7;
int dis[5][2]={0,0,1,0,-1,0,0,1,0,-1};
bool judge()
{
for(int j=1;j<=5;j++)
if(temp[5][j]==0)return false;
return true;
}
void solve(int num,int step)
{
if(step>=ans)return;//step需要小于ans
if(num>5)
{
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
temp[i][j]=data1[i][j];
}
int s=step,i1,j1;
for(int i=1;i<=4;i++)
{
for(int j=1;j<=5;j++)
{
if(!temp[i][j])
{
++s;
i1=i+1;j1=j;
for(int k=0;k<=4;k++)
temp[i1+dis[k][0]][j1+dis[k][1]]^=1;
}
}
}
if(judge()&&ans>s)ans=s;
return;
}
for(int k=0;k<=4;k++)
data1[1+dis[k][0]][num+dis[k][1]]^=1;
solve(num+1,step+1);
for(int k=0;k<=4;k++)
data1[1+dis[k][0]][num+dis[k][1]]^=1;
solve(num+1,step);
return;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
char a;
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
cin>>a;
data1[i][j]=a-'0';
}
}
ans=7;
solve(1,0);
if(ans==7)printf("%d\n",-1);
else printf("%d\n",ans);
}
}
Strange Tower of Hanoi
题目:https://www.acwing.com/problem/content/98/
思路:
写完这道题,你会感受到递推的魅力。
先说一下我的错误思路,这个呢只是针对于我自己以后复习用。看到这道题其实还没有想到思路,我就开始了混乱猜测了,先是把这道题当普通的三个棍子的汉诺塔来做,结果肯定是错的。然后我又进行了多次的乱搞。当我冷静下来时,我还是联想三个棍子的汉诺塔,先把上面的n-1个盘子放到空闲的那根棍子上,再把第n个盘子放到目标棍子上,然后再把n-1个盘子整体放到目标棍子上。那么这道题有四根棍子,是不是也是这个思路,只是方式不一样,我先开始了对半分的尝试,没错就是先把n-1个盘子对半分,分别放到闲置的两根棍子上,然后再把第n个盘子放到目标棍子上,然后再把另外两根棍子上的盘子放到目标棍子上。但还是错,那我没办法了,那我就只能蠢蠢的暴力了,先把前面a个盘子放到某一根棍子上,相当于在四根棍子的辅助下放到目标棍子上,移动次数设为four[a]。再把后面的b个盘子放到某一根棍子上,相当于在三根棍子的辅助下放到目标棍子上,移动次数设为three[b],然后再把第n个盘子放到目标棍子上,再把那b个盘子放到目标棍子上,次数为three[b],再把a个盘子放到目标棍子上,次数为four[a]。
通过以上的思路历程,得到ans=min(1+2*(four[a]+three[b]))。其中three[b]=2*three[b-1]+1
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=20;
int three[maxn],four[maxn];
int main()
{
memset(four,0x3f,sizeof(four));
three[0]=0;four[0]=0;
three[1]=four[1]=1;
printf("1\n");
for(int i=2;i<=12;i++)
{
three[i]=2*three[i-1]+1;
for(int j=1;j<i;j++)
four[i]=min(four[i],2*three[i-1-j]+2*four[j]+1);
printf("%d\n",four[i]);
}
}
Sumdiv
题目:https://www.acwing.com/problem/content/99/
思路:
没错我一开始还是不会,所以只能看答案了。
我一开始先用的质数筛,先找出质数先,然后再分解A,但是这种方法超空间,所以我去搜了答案。没错答案跟我的打法不一样,人家根本没有找质数的操作。分解A的操作是这样一段代码。
ll solve(ll a,ll b)
{
ll ans=1;
ll c,p;
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
{
p=i;
c=0;
while(a%i==0)
{
a/=i;
++c;
}
ans=(ans*sum(p%m,c*b))%m;
}
}
if(a>1)ans=(ans*sum(a,b))%m;
return ans;
}
他这个就比我的好多了(这个是自己写的,但是跟他的打法是一致的)。
首先他找到的约数都是质数,为什么,你想如果这个算法找到了一个合数a1,易得a1肯定能被某个比a小的质数b除断。可以看一下这段代码内部的while循环,这个循环保证了如果一个数可以除断a,那么会一直与之相除直至无法除断,所以找到的约数都是质数。
其次我觉得那个for循环的条件i*i<=a也比较精妙,你想想,如果在i*i<=a中没有数能除断a,那么可定不存在i*i>a且a%i==0的数,这样能少一点循环的次数
代码
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
const int m=9901;
ll solve(ll a,ll b);
ll sum(ll p,ll c);
ll my_pow(ll a,ll b);
int main()
{
ll a,b;
scanf("%lld%lld",&a,&b);
ll ans;
if(a==0)ans=0;
else if(b==0)ans=1;
else ans=solve(a,b);
printf("%lld\n",ans);
}
ll my_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2==1)
ans=(ans*a)%m;
b/=2;
a=(a*a)%m;
}
return ans;
}
ll sum(ll p,ll c)
{
if(c==0)return 1;
if(c%2)
{
ll t1=my_pow(p,(c+1)/2);
ll t2=sum(p,(c-1)/2);
return (((1+t1)%m)*t2)%m;
}
else
{
ll t1=my_pow(p,c/2);
ll t2=sum(p,c/2-1);
ll t3=my_pow(p,c);
return ((((1+t1)%m)*t2)%m+t3)%m;
}
}
ll solve(ll a,ll b)
{
ll ans=1;
ll c,p;
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
{
p=i;
c=0;
while(a%i==0)
{
a/=i;
++c;
}
ans=(ans*sum(p%m,c*b))%m;
}
}
if(a>1)ans=(ans*sum(a,b))%m;
return ans;
}
总结
继续加油