【DP】TEST20170511

来源:互联网 发布:c语言三大应用领域 编辑:程序博客网 时间:2024/05/22 05:14

领土划分(Divide.pas/c/cpp Time:1.5s Memory:64M)

问题描述

    著名的女王,阳姐•查兰•伊丽莎白一世有两个宠物,一个叫大黄,一个叫小昊。女王赐予了这两个宠物一份 N*M 的土地。这片土地由 N*M 个长度为一单位的小正方形构成,每一个小正方形种植水稻或者小麦,但种植的量可能不同。
    大黄喜欢吃饭,也就是说,他希望他的领地里的水稻种植量多,类似的,小昊喜欢吃面,所以他希望他的领地里的小麦种植量多。为了这片土地的划分,大黄与小昊经常发生狗咬狗、相煎何太急的惨剧。
    现在女王决定,派出一辆推土机,这辆推土机从左上角开始,每次向右向下或者向右下移动一个格子,直到走到土地的右下角为止,推土机经过的格子将变为废墟。这样就会将土地分成上下两块,其中右上的那一块归小昊,左的那一块归大黄。
    现在女王想知道,这辆推土机如何移动,能够使得大黄领地中水稻种植总量和小昊领地中小麦种植总量之和最大?

输入

    输入文件名为 Divide.in。
    输入第一行包含两个正整数 N 和 M,表示土地的行数与列数。
    下接一个 N*M 的矩阵描述这块土地的信息,其中,每个格子的信息按照如下方式表示:若该格子种的是水稻,则形式为“JX”,其中 X 为该格子中水稻的种植量;若该格子种的是小麦,则形式为“KX”,其中 X 为该格子中小麦的种植量。
    保证种植量均为小于等于 99 的非负整数,且每一行中相邻两个子之间有逗号隔开。

输出

    输出文件名为 Divide.out。
    输出第一行包含一个正整数,为可能的最大的两种作物种植量总和。

输入样例

4 3K2 K3 K5J3 K1 J1J2 J4 K1K1 K3 J3

输出样例

17

样例解释

    移动方法为向右下,向右下,向下。总和为(3+2+4)+(3+5)=17。

数据范围

40%的数据保证 1<=N,M<=100。
100%的数据保证 1<=N,M<=1500。数据保证有梯度。

分析

    DP

代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int sm = 1500+5;typedef long long LL;int n,m,u;int K[sm][sm],g[sm][sm],J[sm][sm],h[sm][sm];LL f[sm][sm],a,b,c;char ch[10];void turninto(int x,int y) {    u=0;    for(int i=1;i<strlen(ch);++i)        u=u*10+ch[i]-'0';    if(ch[0]=='J')J[x][y]=u;    else K[x][y]=u;}int main() {    freopen("divide.in","r",stdin);    freopen("divide.out","w",stdout);    scanf("%d%d\n",&n,&m);    for(int i=1;i<=n;++i)        for(int j=1;j<=m;++j)            scanf("%s",ch),turninto(i,j);    for(int i=n;i>=1;--i)        for(int j=m;j>=1;--j) {            h[i][j]=h[i+1][j]+J[i+1][j];            g[i][j]=g[i][j+1]+K[i][j+1];        }    for(int i=1;i<=n;++i)        for(int j=1;j<=m;++j) {            a=f[i-1][j-1]+g[i][j]+h[i][j];            b=f[i-1][j]-J[i][j]+g[i][j];            c=f[i][j-1]-K[i][j]+h[i][j];            f[i][j]=max(a,max(b,c));        }    printf("%lld\n",f[n][m]);    return 0;}

扑克游戏(Poker.pas/c/cpp Time:1s Memory:256M)

问题描述

    著名的阳姐•查兰•伊丽莎白一世因为治理国家太有方了,最近国泰民安,阳姐就只能和大黄或者小昊开始玩扑克游戏了。
    这个扑克游戏的规则是这样的。首先,弄出 N(N 保证为偶数)张牌(当我没说扑克吧),每张牌上都有一个分数 Wi,抽到这张牌的人将得到 Wi 的分数。
    将这 N 张牌按照顺序叠成一叠。两个人轮流抽牌,每次都只能抽现存的最上面的牌或者最下面的牌,并且不放回。当牌堆被抽光的时候,分数最高的人胜利。
    因为女士优先,每次都是阳姐先抽。阳姐现在想知道,自己能不能赢,最多能得到多少分,自己得到最高分的时候会抽到原牌堆中的哪些牌。(当两种抽法得到的分数相等时,优先取最上面那张牌)这样说可能会有些不敬,不过由于阳姐出色的(调)教(揉)练,小昊和大黄的智商可以视为极高,也就是说,双方都会执行对自己最有利的抽法。

输入

    输入文件名为 Poker.ni。
    输入第一行一个正整数 N,代表牌堆中牌的个数。
    第二行顺次包含 N 个数,代表牌堆中最上面的牌一直到最下面的牌的分数。

输出

    输出文件名为 Joker.ans。
    输出第一行为“WIN”或“LOSE”,代表赢或者输。
    输出第二行代表阳姐最高的得分。
    输出第三行 N/2 个正整数,代表阳姐得到最高得分的时候,按她抽到的先后顺序,输出她抽到的牌在原来牌堆中的序号。

输入样例

108 4 1 6 3 1 9 7 6 6

输出样例

WIN281 10 8 6 4

数据范围

对于 60%的数据,1<=N<=100。
对于 100%的数据,1<=N<=1000,1<=Wi<=1000。

分析

    记忆化搜索,f[i][j]记区间[i,j]内先手能够取得的最大值,向中间dfs。

代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int sm = 1500+5;int n,a[sm],sum[sm],f[sm][sm];int dfs(int i,int j) {    if(f[i][j])return f[i][j];    if(i==j)return f[i][j]=a[i];    return f[i][j]=sum[j]-sum[i-1]-min(dfs(i+1,j),dfs(i,j-1));}void print(int i,int j,int k) {    if(k==n)return;    if(f[i][j]==sum[j]-sum[i-1]-f[i+1][j]) {        if(k&1)printf("%d ",i);        print(i+1,j,k+1);    }    else     if(f[i][j]==sum[j]-sum[i-1]-f[i][j-1]) {        if(k&1)printf("%d ",j);        print(i,j-1,k+1);    }}int main() {    freopen("poker.in","r",stdin);    freopen("poker.out","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;++i) {        scanf("%d",&a[i]);        sum[i]=sum[i-1]+a[i];    }    dfs(1,n);    if(f[1][n]>sum[n]-f[1][n])printf("WIN\n");    else printf("LOSE\n");    printf("%d\n",f[1][n]);    print(1,n,1);    printf("\n");    return 0;}

罪人审判 (Judge.pas/c/cpp Time:0.4s Memory:64M)

问题描述

    小昊是著名女帝,阳姐•查兰•伊丽莎白一世,的专属,宠物。
    但是小昊人模人样,尤其是公正心,更是超乎正常人一等。所以阳姐委任他担当最高法院最高大法官审判长一职。今天,大法官小昊审判了 N 个罪人,每个人的罪恶度为 Di。按照惯例,这些罪人应该要背着跟他罪恶度相等重量的荆棘游街示众。但是因为小昊的正义过头,导致这几天荆棘几乎卖完了,今天只有M 个单位重量的荆棘了。万一某个罪人少背了 X 个单位的荆棘,那么民众对这个罪人受到的处罚会产生 X 2 的不满值。现在小昊想知道,如何分配仅有的荆棘,才能使民众对这 N 个犯人产生的不满值总和最小?对了,因为小昊的手无法抓稳秤杆,所以每个罪人只能背整数单位的荆棘。

输入

    输入第一行包含两个正整数 M 和 N,意义如上所述。
    下接 N 行,每行一个正整数,为每个罪人的罪恶度 Di。

输出

    输出一行包含一个正整数,代表最小的不满值总和。

输入样例

5 3132

输出样例

1

数据范围

40%的数据,M<=200000。
100%的数据,M,Di<=2.1*10 9 ,N<=10 5 ,保证答案大小在有符号 64 位整型变量存储范围内。

分析

    贪心

代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int sm = 1e5+5;LL n,m,t,u,v,ans,d[sm],pre[sm];LL calc(LL a,int cnt ){    return 1ll*a*a*cnt;}int main() {    freopen("judge.in","r",stdin);    freopen("judge.out","w",stdout);    scanf("%lld%lld",&m,&n);    for(int i=1;i<=n;++i)        scanf("%lld",&d[i]);    sort(d+1,d+n+1);    for(int i=1,p=0;i<=n;++i) {        if(d[i]>p)pre[i]=p,p=d[i];        else pre[i]=pre[i-1];    }    for(int k=n,x=0,y=n;k>0&&y>0;) {        while(d[k-1]==d[y]) --k;        x+=(y-k+1);        if(m/x>=d[k]-pre[k]) {            m-=x*(d[k]-pre[k]);            y=--k;        }        else {            t=m/x;            u=d[k]-t;            v=u-1;            m-=t*x;            ans+=calc(u,x-m);            ans+=calc(v,m);            m=0;--k;            while(k>0)                ans+=calc(d[k--],1);        }    }    printf("%lld\n",ans);    return 0;}