7.31 基本算法1.1

一。递归与递推

方法:  思维模式,找本题所用的思想,找到目标组数,创建数组按顺序依次选择被选择的数---问题边界-----记录该数已选---求解子问题---回溯到上一问题,还原现场

             常与枚举使用

 

例题:ch201 费解的开关(递归实现枚举,状态压缩)

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 int pic[6][6];//存储输入
 6 int tmppic[6][6];//临时复制数组
 7 int di[]={0,0,0,1,-1};
 8 int dj[]={0,1,-1,0,0};
 9 
10 void turn(int i,int j){//反转
11     for(int k=0;k<5;k++){
12         int tmpi=i+di[k],tmpj=j+dj[k];
13         if(tmpi>=1 && tmpi<=5 && tmpj>=1 && tmpj<=5)
14         {
15             if(tmppic[tmpi][tmpj]==1) tmppic[tmpi][tmpj]=0;
16             else tmppic[tmpi][tmpj]=1;
17         }
18     }
19 }
20 
21 bool test(int i){//检测答案
22     for(int j=1;j<=5;j++){
23         if(tmppic[i][j]!=1) return false;
24     }
25     return true;
26 }
27 
28 int solve(){
29     int minans=25;
30     for(int i=0;i<(1<<5);i++){
31         memcpy(tmppic,pic,sizeof(pic));
32         int ans=0;
33         for(int j=0;j<5;j++){
34             if((i>>j)&1) {turn(1,j+1);ans++;}
35         }
36         for(int j=2;j<=5;j++){
37             for(int k=1;k<=5;k++){
38                 if(tmppic[j-1][k]==0){
39                     turn(j,k);
40                     ans++;
41                 }
42             }
43         }
44         if(test(5)) minans=min(minans,ans);
45     }
46     return minans;
47 }
48 
49 int main(){
50     int t;
51     scanf("%d",&t);
52     while(t--){
53         char tmp;
54         for(int i=1;i<=5;i++){
55              for(int j=1;j<=5;j++){
56                  tmp=getchar();
57                  while(tmp!='0'&&tmp!='1') tmp=getchar();
58                  pic[i][j]=tmp-'0';
59              }
60          }
61         int ans=solve();
62         printf("%d\n",ans>6?-1:ans);
63     }
64     return 0;
65 }
View Code

方法:先枚举第一行的开关情况,再每个情况往下一行推看最后一行是否满足要求,则方案数+1

核心:

  for(int j = 0; now; j++, now >>= 1) {

          if(now & 1)    press(0,j);

       }

 

思考:看到这类题先想用递归枚举法是否比dfs要更简便,因为本题只要枚举第一行的情况之后每个情况的答案都只有一种,所以用递归枚举法更方便。先找思想,思维模式考虑是否其实情况答案唯一可以用递归枚举进行状态压缩,若每个情况都会分成更细的子问题则用dfs等搜索更优,不适合再用递归枚举(只适合结果单一不复杂)。

 

 

          poj1845(递归分治)/(数论 逆元+快速幂)

要求的是A^B的所有因子的和之后再mod 9901的值。

因为一个数A能够表示成多个素数的幂相乘的形式。即A=(a1^n1)*(a2^n2)*(a3^n3)...(am^nm)。所以这个题就是要求

(1+a1+a1^2+...a1^n1)*(1+a2+a2^2+...a2^n2)*(1+a3+a3^2+...a3^n2)*...(1+am+am^2+...am^nm) mod 9901。

对于每一个(1+a1+a1^2+...a1^n1) mod 9901

等于 (a1^(n1+1)-1)/(a1-1) mod 9901,这里用到逆元的知识:a/b mod c = (a mod (b*c))/ b

所以就等于(a1^(n1+1)-1)mod (9901*(a1-1)) / (a1-1)。

至于前面的a1^(n1+1),快速幂。‘

 

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cmath>
  4 #include <vector>
  5 #include <string>
  6 #include <cstring>
  7 #pragma warning(disable:4996)
  8 using namespace std;
  9 #define M 9901
 10  
 11 long long p[50005];
 12 int prime[50005];
 13  
 14 void isprime()
 15 {
 16     int cnt = 0, i, j;
 17     memset(prime, 0, sizeof(prime));
 18     
 19     for (i = 2; i < 50005; i++)
 20     {
 21         if (prime[i] == 0)
 22         {
 23             p[++cnt] = i;
 24             for (j = 2 * i; j < 50005;j=j+i)
 25             {
 26                 prime[j] = 1;
 27             }
 28         }
 29     }
 30 }
 31  
 32 long long multi(long long A,long long n,long long k)
 33 {
 34     long long b=0;
 35     while(n>0)
 36     {
 37         if(n&1)
 38         {
 39             b=(b+A)%k;
 40         }
 41         n=n>>1;
 42         A=(A+A)%k;
 43     }
 44     return b;
 45 }
 46  
 47 long long getresult(long long A,long long n,long long k)
 48 {
 49     long long b = 1;
 50     while (n > 0)
 51     {
 52         if (n & 1)
 53         {
 54             b=multi(b,A,k);
 55         }
 56         n = n >> 1;
 57         A=multi(A,A,k);
 58     }
 59     return b;
 60 }
 61  
 62 void solve(long long A, long long B)
 63 {
 64     int i;
 65     long long ans = 1;
 66     for (i = 1; p[i] * p[i] <= A; i++)
 67     {
 68         if (A%p[i] == 0)
 69         {
 70             int num = 0;
 71             while (A%p[i] == 0)
 72             {
 73                 num++;
 74                 A = A / p[i];
 75             }
 76             long long m = (p[i] - 1) * 9901;
 77             ans *= (getresult(p[i], num*B + 1, m) + m - 1) / (p[i] - 1);
 78             ans %= 9901;
 79         }
 80     }
 81     if (A > 1)
 82     {
 83         long long m = 9901 * (A - 1);
 84         ans *= (getresult(A, B + 1, m) + m - 1) / (A - 1);
 85         ans %= 9901;
 86     }
 87     cout << ans << endl;
 88 }
 89  
 90 int main()
 91 {
 92     //freopen("i.txt","r",stdin);
 93     //freopen("o.txt","w",stdout);
 94     
 95     long long A, B;
 96     
 97     isprime();
 98     
 99     while (cin>>A>>B)
100     {
101           solve(A, B);
102     }
103     return 0;
104 }
View Code

 

#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod=9901;
int pow_mod(int a,int b)
{
    a=a%mod;
    int s=1;
    while(b)
    {
        if(b&1)
            s=(s*a)%mod;
        a=(a*a)%mod;
        b=b>>1;
    }
    return s;
}
int sum(int a,int b)//求1+a+a^2+...+a^b
{
    if(b==1)return 1;
    if(b&1)return (sum(a,b/2)*(1+pow_mod(a,b/2+1))+pow_mod(a,b/2))%mod;
    else return sum(a,b/2)*(1+pow_mod(a,b/2))%mod;
}
int main()
{
    int a,b;
    while(cin>>a>>b)
    {
         if(a<=1||b==0){cout<<1<<endl;continue;}
        int ans=1,i,j,k,t,n,m;
        n=(int)sqrt(a+0.5);
        for(i=2;i<=n;i++)
        {
            if(a%i==0)
            {
                t=0;
                while(a%i==0){
                    a=a/i;
                    t++;
                }
                ans=ans*sum(i,t*b+1)%mod;
            }
        }
        if(a>1)
            ans=ans*sum(a,b+1)%mod;
        cout<<(ans+mod)%mod<<endl;    
    }
    return 0;
}
View Code

 

思考:看到这类偏数学的题立马想思维,想数学公式与逆元,快速幂等数学方法,手写推导,此题需要考虑次方的奇偶性

 

          poj3889(分形)(坐标变换)

参考:https://blog.csdn.net/ben_xsy/article/details/79288058

         

 

posted @ 2019-08-01 10:24  zyddd915  阅读(152)  评论(0编辑  收藏  举报