2017.3.25 机房测试(数位DP,状压DP,矩阵)

来源:互联网 发布:windows平板电脑 i5 编辑:程序博客网 时间:2024/04/28 14:46

cky远在上海仍不忘为我们出题。。。
这次是DP专题。。。

天平(balance.in/balance.out)

物理老师YJ有一个长杆天平,天平的两臂长均为15,将长杆看作x轴,则平衡点在0位置处,负数位置在左臂上,正数位置在右臂上。长杆上有n个位置有挂钩可以挂秤砣。YJ有m个秤砣,质量分别为gi,每个挂钩可以不挂也可以挂任意个秤砣。YJ想要知道,在使用所有秤砣的条件下,有多少种不同的挂秤砣的方案,可以使得天平平衡?问题太过复杂,仅凭物理知识难以解决,所以请你来帮助他。
天平的平衡条件是所有秤砣的位置质量之和为0。例如有质量为2,3,4的秤砣分别挂在-3,-2,3位置处,则2(-3) + 3*(-2) + 4*3 == 0,天平是平衡的。
【输入格式】
第一行两个数n,m。表示挂钩的数目和秤砣的数目。
第二行n个不同且递增的数,第i个数表示第i个挂钩的位置,数的范围在[-15,15]内。
第三行m个不同且递增的数,第i个数表示第i个秤砣的质量,数的范围在[1,25]内。
【输出格式】
一个整数,代表能使得天平平衡的方案数。
【输入样例】
2 4
-2 3
3 4 5 8
【输出样例】
2
【样例解释】
方案1: (-2)*(3+4+5) + 3*8 = 0
方案2: (-2)(4+8) + 3(3+5) = 0
【数据规模】
10% 数据满足2≤n,m≤4。
100% 数据满足2≤n,m≤20。
【解题报告】

代码如下:

#include<cstdio>#include<cstring>#include<algorithm>#include<time.h>using namespace std;long long n,m,ans=0;long long f[25][15001]={0},c[25],r[25];int main(){    freopen("balance.in","r",stdin);    freopen("balance.out","w",stdout);    scanf("%I64d%I64d",&n,&m);    for(int i=1;i<=n;i++)    scanf("%I64d",&c[i]);    for(int i=1;i<=m;i++)    scanf("%I64d",&r[i]);    f[0][7500]=1;    for(int i=1;i<=m;i++)    for(int k=1;k<=n;k++)    for(int j=0;j<=15000;j++)    {        if(j-c[k]*r[i]<0||j-c[k]*r[i]>15000) continue;        f[i][j]+=f[i-1][j-c[k]*r[i]];    }    printf("%I64d",f[m][7500]);    return 0;}

山峰数(hill.in/hill.out)

山峰数是指数字排列中不存在山谷(先降后升)的数,例如0,5,13,12321都是山峰数,101,1110000111都不是山峰数。
现给出n个数,请依次判断它们是否为山峰数,如果不是,输出-1。如果是,求出比它小的数中有多少个山峰数。
【输入格式】
第一行一个数n,表示询问数目。
接下来n行,每一行¬¬¬一个数x,表示询问的数。
【输出格式】
输出有n行,x如果不是山峰数,输出-1。x如果是山峰数,则输出有多少个比它小的山峰数。
【输入样例】
5
10
55
101
1000
1234321
【输出样例】
10
55
-1
715
94708
【数据规模】
20% 数据满足x ≤ 106。
100% 数据满足n ≤ 10, x ≤ 1060
【解题报告】

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;long long f[75][10][2][2];int len,a[75];char str[105];long long dfs(int p,int nu,int isdown,int lim) {    if(p==len+1)return 1;    if(f[p][nu][isdown][lim]!=-1)return f[p][nu][isdown][lim];    long long tmpans=0;    int r=lim?a[p]:9;    for(int i=0;i<=r;i++)     {        if(!isdown)        {            if(i>=nu) tmpans+=dfs(p+1,i,0,lim && i==r);            else tmpans+=dfs(p+1,i,1,lim && i==r);        }        else if (i<=nu) tmpans+=dfs(p+1,i,1,lim && i==r);    }    return f[p][nu][isdown][lim]=tmpans;}int main(){    freopen("hill.in","r",stdin);    freopen("hill.out","w",stdout);    int tc;    scanf("%d\n",&tc);    while(tc--)    {        scanf("%s",str);        len=strlen(str);        for(int i=0;i<len;i++)        a[i+1]=str[i]-'0';        bool isdown=false;        bool ishill=true;        for(int i=2;i<=len;i++)        {            if(a[i]<a[i-1])            isdown=true;            if(isdown && a[i]>a[i-1])            {                printf("-1");                ishill=false;                break;            }        }        if(ishill)         {            memset(f,-1,sizeof(f));            printf("%I64d",dfs(1,0,0,1)-1);        }        if(tc!=0)        printf("\n");    }    return 0;}

粉刷匠2(draw.in/draw.out)

有一个4*N的木板需要粉刷,第i行j列的颜色记为A(i, j)。
有256种颜色,记为0..255,为了使得粉刷比较好看,粉刷需要满足如下要求:
1. A(x,y) >= A(x,y-1)
2. 有一些指定的(x1, y1)和(x2, y2),要求A(x1, y1) = A(x2, y2)
请问有多少种满足要求的粉刷方式?输出答案的最后5位即可。
【输入格式】
第一行两个数n, m,表示木板的长度,和指定规则的条目个数。
接下来m行,每行四个数x1,y1,x2,y1,此规则表示A(x1, y1) 需要等于 A(x2, y2)
【输出格式】
一个整数,表示答案的末5位。
【输入样例1】
1 0
1 3
【输出样例1】
67296
【输入样例2】
1 3
1 1 2 1
1 1 3 1
4 1 3 1
【输出样例2】
00256
【提示】
输出可以使用%05d输出。
【数据规模】
30% 数据满足 n ≤ 3,m = 0
100% 数据满足1 ≤ n ≤ 15,0 ≤ M ≤ 100,X1,X2≤4,Y1,Y2≤n
【解题报告】

代码如下:

#include<cstdio>#include<cstring>#define fi first #define se second#define ll long long#define ms(x,y) memset(x,y,sizeof(x))using namespace std;int f[16][16][16][16];bool vis[16][16][16][16];int r1x[105],r2x[105],r1y[105],r2y[105];int main(){     freopen("draw.in","r",stdin);    freopen("draw.out","w",stdout);    int n,m,tc;    int c[4];    tc=1;    for(int tt=1;tt<=tc;tt++)    {        ms(vis,0);        scanf("%d%d",&n,&m);        for(int i=1;i<=m;i++){            scanf("%d%d%d%d",&r1x[i],&r1y[i],&r2x[i],&r2y[i]);            r1x[i]--;r2x[i]--;        }        for(int i=1;i<=m;i++){            for(c[0]=0;c[0]<=n;c[0]++)            for(c[1]=0;c[1]<=n;c[1]++)            for(c[2]=0;c[2]<=n;c[2]++)            for(c[3]=0;c[3]<=n;c[3]++){                if(c[r1x[i]]>=r1y[i] ^ c[r2x[i]]>=r2y[i])                    vis[c[0]][c[1]][c[2]][c[3]]=1;            }        }        ms(f,0);        f[0][0][0][0]=1;        for(int color=0;color<=255;color++){            for(int col=0;col<=3;col++)            for(c[0]=0;c[0]<=n;c[0]++)            for(c[1]=0;c[1]<=n;c[1]++)            for(c[2]=0;c[2]<=n;c[2]++)            for(c[3]=0;c[3]<=n;c[3]++)                if(c[col]<n){                    int tmp=f[c[0]][c[1]][c[2]][c[3]];                    c[col]++;                    f[c[0]][c[1]][c[2]][c[3]] = (f[c[0]][c[1]][c[2]][c[3]] + tmp) % 100000;                    c[col]--;                }            for(c[0]=0;c[0]<=n;c[0]++)            for(c[1]=0;c[1]<=n;c[1]++)            for(c[2]=0;c[2]<=n;c[2]++)            for(c[3]=0;c[3]<=n;c[3]++)                if(vis[c[0]][c[1]][c[2]][c[3]])                    f[c[0]][c[1]][c[2]][c[3]]=0;        }        printf("%05d\n",f[n][n][n][n]);    }    return 0;}

棋盘(knight.in/knight.out)

有一个N*M的棋盘,要在上面摆上knight,每个格子可以放至多一个knight。
knight的攻击范围如下图:
这里写图片描述
所有knight不能互相攻击,请问总共有多少可行的摆法?答案对1000000007取模。
【输入格式】
第一行个数t,表示测试的组数。
接下来t组,每组两个整数,n和m。
【输出格式】
一共t行,第i行表示第i组的答案。
【输入样例】
4
1 2
2 2
3 2
3 31415926
【输出样例】
4
16
36
933912086
【数据规模】
70% 数据满足m≤100。
100% 数据满足t≤10, n≤3, m≤109。

代码如下:

#include<cstdio>#include<cstring>#include<iostream>#define ms(x,y) memset(x,y,sizeof(x))#define ll long longusing namespace std;const ll mod=(1e9)+7;int n,m;int fi[8]={-2,-1,1,2,2,1,-1,-2};int fj[8]={1,2,2,1,-1,-2,-2,-1};int lim;typedef ll arr[1<<8][1<<8];arr A,B,s,tmparr;int p[3][4];arr storea[5];arr stores[5];bool isstore[4];bool check(int state) {    ms(p,0);    int tmp=state;    for(int i=1;i>=0;i--)        for(int j=m-1;j>=0;j--,tmp>>=1)            p[i][j]=tmp&1;    for(int i=0;i<2;i++)        for(int j=0;j<m;j++)            if(p[i][j])                for(int d=0;d<8;d++) {                    int ti=i+fi[d],tj=j+fj[d];                    if(ti>=0 && tj>=0 && ti<=2 && tj<m){                        if(ti<2 && p[ti][tj]) return false;                        if(ti==2) p[ti][tj]=1;                    }                }    int line3=0;    for(int j=0;j<m;j++)        line3=(line3<<1)+p[2][j];    int limm=(1<<m)-1;    line3=(~line3)&limm;       for(int i=0;i<=limm;i++)        if((i|line3)==line3)            A[state][i+((state&limm)<<m)]=1;    return true;}void Multi(arr A,arr B,arr C) {    ms(tmparr,0);    for(int i=0;i<=lim;i++)        for(int j=0;j<=lim;j++)            for(int k=0;k<=lim;k++)                tmparr[i][j]=(tmparr[i][j]+A[i][k]*B[k][j])%mod;    for(int i=0;i<=lim;i++)        for(int j=0;j<=lim;j++)            C[i][j]=tmparr[i][j];}int main(){    freopen("knight.in","r",stdin);    freopen("knight.out","w",stdout);    int tc;    ms(isstore,0);    scanf("%d\n",&tc);    while(tc--){        scanf("%d%d",&m,&n);        if(n==1){            printf("%d\n",1<<m);            continue;        }        ms(A,0);ms(s,0);ms(B,0);        n-=2;        lim=(1<<2*m)-1;        if(!isstore[m]) {            for(int i=0;i<=lim;i++) {                if(check(i))s[0][i]=1;                else s[0][i]=0;            }            for(int i=0;i<=lim;i++)                for(int j=0;j<=lim;j++)                {                    stores[m][i][j]=s[i][j];                    storea[m][i][j]=A[i][j];                }            isstore[m]=true;        }        else{            for(int i=0;i<=lim;i++)                for(int j=0;j<=lim;j++)                {                    s[i][j]=stores[m][i][j];                    A[i][j]=storea[m][i][j];                }        }        for(int i=0;i<=lim;i++)            B[i][i]=1;        while(n) {            if(n&1)Multi(B,A,B);            Multi(A,A,A);            n>>=1;        }        Multi(s,B,s);        ll ans=0;        for(int i=0;i<=lim;i++)            ans=(ans+s[0][i])%mod;        cout<<ans<<endl;    }    return 0;}
0 0
原创粉丝点击