动态规划测试test20170520

17 篇文章 0 订阅

前言

这次考试幸好看过棋盘型dp的相关文章(然后发现第三题做过),于是第一题在打表找规律下加乱搞得到递推式,然后就发现还要高精,然后打压位高精过了。第二题明知道是转化为单调队列下的多重背包处理,可是不会打,然后就爆0了。

试题

罗大师的棋盘(chessboard.pas/c/cpp)

Time:0.05s Memory:256M

【问题描述】

罗大师有一个 8*8 的棋盘。有一天罗大师有点无聊,于是想将这个切割这个棋盘。每次
将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割
了(n-1)次后,连同最后剩下的矩形棋盘共有 n 块矩形棋盘。(每次切割都只能沿着棋盘格子
的边进行)。原棋盘的每个格子上都有一个权值,剩下的矩形棋盘的总分为其所含各个格子
的权值之和。罗大师希望分得的 N 个矩形棋盘的权值相差比较小,也就是均方差最小。罗
大师想知道,最小的可能的均方差为多少?

【输入】

输入文件名为 chessboard.in。
输入一个数 N,代表罗大师要切割成的块数。
后接一个 8*8 的矩阵,表示每个格子上的

【输出】

输出文件名为 chessboard.out。
输出一个实数,为最小的可能的均方差。(四舍五入到小数点后 3 位)

【输入输出样例】

chessboard.in
3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3
chessboard.out
1.633

【数据范围】

测试点编号 N 的值
1       3
2       4
3       5
4       6
5       7
6       8
7       9
8       10
9       11
10       12
并且保证每个格子上的权值为一个不大于 100 的非负整数。

【题解】

我们可以很容易知道方差的公式其实是可以化解的为 S2=1n(ini=1a2inx¯2)

要使方差最小,只需使 xi2 最小即可,即各块分值平方和最小。 x¯ 是个固定的数,跟如何分割无联系.

首先定义状态: dp[k][x1][y1][x2][y2] ,表示左上角坐标 (x1,y1) 到右下角坐标 (x2,y2) 区域的棋盘经过k次分割后,各块分值平方和的最小值。则状态转移方程为:

dp[k][x1][y1][x2][y2]=mindp[0][x1][y1][t][y2]+dp[k1][t+1][y1][x2][y2],(x1tx2)dp[k1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2],(x1tx2)dp[0][x1][y1][x2][t]+dp[k1][x1][t+1][x2][y2],(y1ty2)dp[k1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2](y1ty2)

dp[0][x1][y1][x2][y2] 表示左上角坐标 (x1,y1) 到右下角坐标 (x2,y2) 区域的棋盘的分值和平方

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))

const int size = 10;
const int INF = 1<<30;
int num[size][size];
int sum[size][size];
double dp[14][size][size][size][size];
int n,tot;
double ans;

//返回左上角坐标(x1,y1)到右下角坐标(x2,y2)区域的棋盘的分值和平方
inline double calc(int x1,int y1,int x2,int y2) {
    double ret=(double)(sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
    return ret*ret;
}

int main() {
    freopen("chessboard.in","r",stdin);
    freopen("chessboard.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=8;i++) {
        for(int j=1;j<=8;j++) {
            scanf("%d",&num[i][j]);
            //sum[i][j]表示棋盘(1,1)到(i,j)区域的累计分值
            sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+num[i][j];
            //tot表示整个棋盘的分值之和
            tot+=num[i][j];
        }
    }

    //初始化dp
    for(int x1=1;x1<=8;x1++)
        for(int y1=1;y1<=8;y1++)
            for(int x2=x1;x2<=8;x2++)
                for(int y2=y1;y2<=8;y2++)
                    dp[0][x1][y1][x2][y2]=calc(x1,y1,x2,y2);

    for(int k=1;k<n;k++)
        for(int x1=1;x1<=8;x1++)
            for(int y1=1;y1<=8;y1++)
                for(int x2=x1;x2<=8;x2++)
                    for(int y2=y1;y2<=8;y2++) {
                        dp[k][x1][y1][x2][y2]=(double)INF;
                        for(int t=x1;t<x2;t++) dp[k][x1][y1][x2][y2] = Min(Min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2]),dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2]);
                        for(int t=y1;t<y2;t++) dp[k][x1][y1][x2][y2] = Min(Min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2]),dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2]);
                    }

    //计算方差平方
    double ans = dp[n-1][1][1][8][8]*1.0/n-((double)tot*1.0/n)*((double)tot*1.0/n);
    printf("%.3lf\n",sqrt(ans));
    return 0;
}

举火燎天(lights.pas/c/cpp)

Time:1s Memory:256M

【问题描述】

征夷王将越南(南夷)攻下来之后,决定用 N 个火炬摆成一个圆圈,围住越南的都城,
以举火燎天宣告自己的胜利。这 N 个火炬的位置已经固定了,但是,每个火炬的颜色都没
定,总共有 M 种颜色的火炬。为了显示我们中华文化的博大精深,征夷王决定,相邻的火
炬的颜色不能相同。现在征夷王想知道,满足他要求的方案有多少个。由于火炬的位置已经
固定,所以即便旋转翻转之后两种方案相同也视作不同方案。

【输入】

输入文件名为 lights.in。
输入一行两个正整数 N 和 M,意义如上所述。

【输出】

输出文件名为 lights.out。
输出一行一个正整数,代表方案总数。

【输入输出样例】

lights.in
3 4
lights.out
24

【数据范围】

对于 100%的数据, 1N,M100

【题解】

f[i][1] 表示第 i 个火炬与第 1 个火炬颜色相同的方案个数,而 f[i][0] 表示第 i 个火炬与第 1 个火炬颜色不同的方案个数。

f[1][0]=0f[1][1]=m

f[i][0]=f[i1][1](m1)+f[i1][0](m2)

f[i][1]=f[i1][0]

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
typedef long long LL;

const LL Mod = 100000000;

struct Bignum {
    LL a[110];
    int len ;

    Bignum (LL x = 0) {
        this->clear();a[0]=x;
    }

    void print() {
        printf("%lld",a[len-1]);
        for(int i=len-2;i>=0;i--) 
            printf("%08lld",a[i]);
        puts("");
    }

    void clear() {
        memset(a,0,sizeof a);
        len = 1;
    }

    Bignum operator + (const Bignum &h) {
        Bignum ans;
        ans.len = Max(len,h.len)+1;
        for(int i=0;i<ans.len;i++) {
            ans.a[i] = ans.a[i]+h.a[i]+a[i];
            ans.a[i+1] += ans.a[i]/Mod;
            ans.a[i] %= Mod;
        }
        if(!ans.a[ans.len-1])
            --ans.len;
        return ans;
    }

    Bignum operator * (const Bignum &h) {
        Bignum ans;
        ans.len = len+h.len;

        for(int i=0;i<len;i++) 
            for(int j=0;j<h.len;j++) 
                ans.a[i+j] = a[i]*h.a[j];
        for(int i=0;i<ans.len;i++) {
            ans.a[i+1] +=ans.a[i]/Mod;
            ans.a[i] %= Mod;
        }
        if(!ans.a[ans.len-1])
            --ans.len;
        return ans;
    }


}f[120][2],s1,s2;

LL n,m;

int main() {
    freopen("lights.in","r",stdin);
    freopen("lights.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    if(n==1) {
        printf("%lld\n",m);
        return 0;
    }

    f[1][1].a[0]=m;
    s1.a[0]=m-1;s2.a[0]=m-2;

    for(int i=2;i<=n;i++) {
        f[i][1]=f[i-1][0];
        f[i][0]=f[i-1][0]*s2+f[i-1][1]*s1;
    }

    f[n][0].print();

    return 0;
}

罗大师的采药(medic.pas/c/cpp )

Time:0.2s Memory:256M)

【问题描述】

罗大师掉到了一个山洞,里面有 N 株药材。但是罗大师要教济远小朋友题目,所以他
必须赶快离开。但是罗大师需要采药来赚点外快。采某株药需要 Ti 个单位时间,同时这株
草药价值 Vi 个金币。罗大师为了考验高二小朋友,所以就提出了 Q 个问题,每个询问是“如
果我只有 Mi 个单位时间,我最多能得到多少个金币?”现在,为了高二机房的尊严,请你
们回答罗大师的询问。

【输入】

输入文件名为 medic.in。
输入文件第一行为 N 和 Q,代表有 N 株草药和 Q 个询问。
后接 N 行,每行两个整数,分别为采第 I 株草药的时间 Ti 和其价格 Vi。
后街 Q 行,每行一个正整数 Mi,代表对于每个询问。

【输出】

输出文件名为 medic.out。
输出 Q 行,每行一个整数,代表对于罗大师的每个询问你得出的答案。

【输入输出样例】

medic.in
3 1
10 10
8 1
1 2
9
medic.out
3

【数据范围】

对于 50%的数据, 1N,Mi,Q1000 ,;
对于 100%的数据, 1N,Mi,Q100000 Ti,Vi10

【题解】

转化为单调队列下的多重背包处理

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>

template< typename Type >inline void Read( Type &In ) {
    In=0;
    char ch=getchar();
    for( ; ch> '9'||ch< '0'; ch=getchar() );
    for( ; ch>='0'&&ch<='9'; ch=getchar() )In = In*10 + ch-'0';
}

const int maxn = 1e5+10;

int n , q , t , v , x , c , M , tmp;
int Que[maxn][2] , H , T;
int m[maxn] , Sz[11][11];
int f[maxn];


int main() {

    freopen("medic.in","r",stdin);
    freopen("medic.out","w",stdout);
    Read( n );
    Read( q );
    For( i , 1 , n )Read( t ) , Read( v ) , Sz[t][v]++;
    For( i , 1 , q )Read( m[i] ) , M = max( M , m[i] );
    for(int i=1; i<=10; i++)
        for(int j=1; j<=10; j++)
            if( Sz[i][j] ) {
                t = i;
                v = j;
                x = M / t;
                c = std::min( x , Sz[i][j] );
                for(int u=0; u<=t-1; u++) {
                    H = T = 0;
                    Que[0][1] = f[u] + x*v;
                    for(int k=1; k<=x; k++) {
                        int tmp = j + k*t;
                        if( tmp > M )break;
                        while( H <= T && k - Que[H][0] > c )H++;
                        while( H <= T && Que[T][1] <= f[ tmp ] + ( x-k )*v )T--;
                        T++;
                        Que[T][0] = k;
                        Que[T][1] = f[ tmp ] + ( x-k )*v;
                        f[ tmp ] = std::max( f[ tmp ] , Que[H][1] - ( x-k )*v );
                    }
                }
            }
    for(int i=1; i<=q; i++)
        printf("%d\n",f[m[i]]);
    return 0;
}

总结

其实这次考试还算不错吧,但其实讲过的题都不会打,还是需要落实啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值