7.11 DP结课测

T1:

题目链接:https://www.luogu.org/problemnew/show/U30475

题意:给定一个数a和n个整数,从n个数中选取几个数x1,x2...xr(可不连续),使a%x1%x2%...%xr==0,

求出最小的r,也就是最少的数字个数。

做这个题,没理解好题意,把选取的数当作必须是连续的了,

下面是代码,碰运气拿了20分。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 
 8 int t,a,n,b[22],sum[22],ans;
 9 bool flag;
10 
11 int main()
12 {
13     freopen("mod.in","r",stdin);
14     freopen("mod.out","w",stdout);
15     scanf("%d",&t);
16     for(int i=1;i<=t;++i)
17     {
18         scanf("%d%d",&n,&a);
19         for(int j=1;j<=n;++j)
20         {
21             scanf("%d",&b[j]);
22             if(a%b[j]==0) 
23                 flag=1;
24         }
25         if(flag==1)
26         {
27             flag=0;
28             printf("1
");
29             continue;
30         }
31         int k=1;
32         memset(sum,0,sizeof(sum));
33         while(k<=n)
34         {
35             for(int j=k;j<=n;++j)
36             {
37                 sum[k]+=b[j];
38                 if(a%sum[k]==0)
39                 {
40                     flag=1;
41                     ans=j-k+1;
42                     break;
43                 }
44             }
45             if(flag==1)
46             {
47                 flag=0;
48                 printf("%d
",ans);
49                 break;
50             }
51             k++;
52             if(k>n)
53             {
54                 printf("-1
");
55                 break;
56             }
57         }
58     }
59     return 0;
60 }
View Code

正解:

对于一个%两次就能%完的数来说,先%大还是先%小是无所谓的,

但对于大多数来说,在%一个数不能%完之后需要%的数一定比第一次%的要小,直到%完为止。

这就是思路关键。

所以,此题便可先从大到小排序,后面比a大的数可以直接略掉了,然后用dfs深搜可以%的数,比较筛选出最少的数字个数就好,

如果搜完之后数字个数仍然是极大值,那说明不可以,就输出“-1”,结束。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define inf 10000000
 6 using namespace std;
 7 int T,x,n,ans;
 8 int a[110];
 9 int init()
10 {
11     int x=0,f=1;char c=getchar();
12     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
13     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
14     return x*f;
15 }
16 void dfs(int w,int t,int now)
17 {
18     if(t>=ans)return;
19     if(now==0){ans=t;return;}
20     if(w==n+1)return;
21     dfs(w+1,t,now);
22     dfs(w+1,t+1,now%a[w]);
23 }
24 int cmp(int a,int b)
25 {
26     return a>b;
27 }
28 int main()
29 {
30     //freopen("mod.in","r",stdin);
31     //freopen("mod.out","w",stdout);
32     T=init();
33     while(T--)
34     {
35         n=init();x=init();
36         for(int i=1;i<=n;i++)
37           a[i]=init();
38         sort(a+1,a+n+1,cmp);
39         ans=inf;
40         dfs(1,0,x);
41         if(ans==inf) printf("-1
");
42         else printf("%d
",ans);
43     }
44     //fclose(stdin);fclose(stdout);
45     return 0;
46 }

T2:

题目链接:https://www.luogu.org/problemnew/show/U30477

题意:给定一个NxM的矩阵,要在上面放若干个“炮”,问有多少种放法。

放置要求:如果两个“炮”中间还有一个“炮”(不一定两个夹着一个,两个之间还可以有别的空格),那么这两个“炮”就会发生攻击,但要求不能发生攻击,也可以不放。

做的时候没有想到正解那么神的思路,手算也能过一些样例,所以打了表。

通过要求还是可以知道一行或着一列中,可以不放,可以放一个,可以放两个,最多放两个,如果放三个,两个中间必定有一个“炮”,那两边的两个必定会发生攻击,此时便不符合要求,所以一行或着一列的状态便只有不放,一个,两个三种状态,那就可以设一个三维数组dp[i][j][k],

i表示行,j表示放一个的列数,k表示放两个的列数,m-j-k+1就是不放的列数,累计每行中放的列数不能超过3,每列中放的行数也一样,

所以就能得出转移方程。

ac代码:

#include<cstdio>
#include<iostream>
#define mod 999983LL
#define N 110
using namespace std;

long long f[N][N][N],n,m;

int main()
{
    //freopen("cannon.in","r",stdin);
    //freopen("cannon.out","w",stdout);
    cin>>n>>m;
    f[0][0][0]=1;
    for(long long i=1;i<=n;i++)
      for(long long j=0;j<=m;j++)
        for(long long k=0;j+k<=m;k++)
        {
            f[i][j][k]=f[i-1][j][k];f[i][j][k]%=mod;//不放
            if(j>=1) f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1);//空处放1个
            if(k>=1) f[i][j][k]+=f[i-1][j+1][k-1]*(j+1);//1个处放1个
            if(j>=2) f[i][j][k]+=f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2;//空处放2个
            if(k>=2) f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2;//1处放2个
            if(k>=1) f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1);//空处和1个处
            f[i][j][k]%=mod;
        }
    long long ans=0;
    for(long long j=0;j<=m;j++)
      for(long long k=0;j+k<=m;k++)
        ans+=f[n][j][k],ans%=mod;
    cout<<ans;
    //fclose(stdin);fclose(stdout);
    return 0;
}

T3:

题目链接:https://www.luogu.org/problemnew/show/U30480

题意:一个n*m矩阵,矩阵中每个元素都有一个相应的高度,矩阵最上面是一个湖泊,最下面是沙漠,在矩阵区域建立蓄水厂和输水站,要求靠近沙漠的那一行必须都有输水站,而能建造输水站的前提,是存在比它更高且拥有公共边的相邻位置,并且蓄水厂也只能建立在靠近湖泊的那一行中。问能是否满足要求,能的话,求出最少要建立几个蓄水厂,不能的话,求靠近沙漠的一行中不可能建有输水站的个数。

这题a掉了,题意很明确,判断能否满足要求用dfs搜索是否能到达靠近沙漠的最底下一行是否都可以有输水管道到达就好了。

然后若能满足,就需要就出第一行要建立蓄水厂的个数,有一个思路就是可以倒过来想,已经满足最后一行都有了输水站,那么就顺着能够到达的输水管道回去,所以还是用搜索做,类似于一个线段覆盖,线段覆盖的前提是,所到达的点所覆盖的线段一定是连续的,证明这一点也是此题的关键,因为如果不连续,那么这个点就无法到达,所以,只要求出每个点能到达的最左和最右的点就可以了,这个点不会变,这样的话,就可以利用之前用的搜索,加一个记忆化处理,记录能到达最后一行时第一行可以建立蓄水站的个数就好了。

ac代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

int n,m;
int l[502][502],r[502][502],a[502][502];
int xx[4]= {-1,0,1,0},yy[4]= {0,1,0,-1};
int qx[502*502],qy[502*502];
bool b[502][502];

void dfs(int x,int y)
{
    b[x][y]=1;
    for(int i=0; i<4; i++)
    {
        int dx=x+xx[i],dy=y+yy[i];
        if(dx<1||dx>n||dy<1||dy>m||a[dx][dy]>=a[x][y]) 
            continue;
        if(!b[dx][dy]) 
            dfs(dx,dy);
        l[x][y]=min(l[x][y],l[dx][dy]);
        r[x][y]=max(r[x][y],r[dx][dy]);
    }
}

int main()
{
    //freopen("flow.in","r",stdin);
    //freopen("flow.out","w",stdout);
    scanf("%d%d",&n,&m);
    memset(b,0,sizeof(b));
    memset(l,0x3f,sizeof(l));
    memset(r,0,sizeof(r));
    for(int i=1; i<=m; i++)
        l[n][i]=r[n][i]=i;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            scanf("%d",&a[i][j]);
    for(int i=1; i<=m; i++)
        if(!b[1][i]) dfs(1,i);
    bool flag=0;
    int ans=0;
    for(int i=1; i<=m; i++)
        if(!b[n][i])
        {
            flag=1;
            ans++;
        }
    if(flag)
    {
        printf("0
");
        printf("%d",ans);
        return 0;
    }
    int left=1;
    while(left<=m)
    {
        int maxn=0;
        for(int i=1; i<=m; i++)
            if(l[1][i]<=left)
                maxn=max(maxn,r[1][i]);
        ans++;
        left=maxn+1;
    }
    printf("1
");
    printf("%d",ans);
}

如果你不开心,那我就把右边这个帅傻子分享给你吧,
你看,他这么好看,跟个zz一样看着你,你还伤心吗?
真的!这照片盯上他五秒钟就想笑了。
一切都会过去的。
时间时间会给你答案2333
原文地址:https://www.cnblogs.com/Mary-Sue/p/9294840.html