动态规划测试test20170511

来源:互联网 发布:淘宝紫砂壶店铺 编辑:程序博客网 时间:2024/05/16 23:37

前言

这次考试大家似乎都很厉害的样子,差不多都是A掉两个题,只是有些A了1,3题,有的A了1,2题。
然后发现其实这三个题目都其实并不是很难。

题目

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

【问题描述】

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

【输入】

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

【输出】

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

【输入输出样例】

Divide.in
4 3
K2 K3 K5
J3 K1 J1
J2 J4 K1
K1 K3 J3
Divide.out
17

【样例解释】

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

【数据范围】

40%的数据保证 1N,M100
100%的数据保证 1N,M1500。数据保证有梯度。

【题解】

研究一下推土机的移动轨迹,发现这个推土机其实是单调地向左移动,不会向右移动。那么此时就可以dp了

用f[i][j]表示走到(i,j)的时候得到的最大种植量
有转移方程:
f[i][j]=min( f[i1][j1]+SumJ[i][j1]+SumK[i1][j],f[i1][j]+SumJ[i][j1],f[i][j1]+SumK[i1][j] )
SumJ[i][j]表示第i行前j列种植水稻的和
SumK[i][j]表示第j列前i行种小麦的和
然后这一题目的读入还是比较的坑的,注意一下。

【代码】

#include <cstdio>#include <cstring>#include <algorithm>#define Max(a,b) ((a)>(b)?(a):(b))#define Min(a,b) ((a)<(b)?(a):(b))const int inf = -2147483467;const int size = 1500+10;int x[size][size],y[size][size];int f[size][size];int n,m;inline void read(int i,int j) {    int in=0;    char ch1=getchar();    while(ch1!='K'&&ch1!='J')        ch1=getchar();    char ch2=getchar();    for(;ch2>='0'&&ch2<='9';ch2=getchar())        in=in*10+ch2-'0';    if(ch1=='K')        y[i][j]=in;    if(ch1=='J')        x[i][j]=in;}int main() {    freopen("divide.in","r",stdin);    freopen("divide.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            read(i,j);    for(int i=1;i<=n;i++) {        for(int j=2;j<=m;j++) {            x[i][j]+=x[i][j-1];        }    }    for(int i=1;i<=n;i++) {        for(int j=m-1;j>=1;j--) {            y[i][j]+=y[i][j+1];        }    }    for(int i=0;i<=m;i++)        f[1][i] = y[1][i+1];    f[1][0] = inf;    for(int i=2;i<=n;i++)        for(int j=1;j<=m;j++)            f[i][j]=Max(Max(f[i-1][j],f[i-1][j-1])+x[i][j-1]+y[i][j+1],f[i][j-1]+y[i][j+1]-y[i][j]);    printf("%d\n",f[n][m]);    return 0;}

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

【题目描述】

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

【输入】

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

【输出】

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

【输入输出样例】

Poker.in
10
8 4 1 6 3 1 9 7 6 6
Poker.ans
WIN
28
1 10 8 6 4

【数据范围】

对于 60%的数据,1N100
对于 100%的数据,1N10001Wi1000

【题解】

f[i][j]代表当取到[I,J]这一段时先手所能获得的最高分。
那么f[i][j]=Sum[I][J]Minf[i][j1],f[i+1][j]

仔细去想觉得吧,只有从后往前抽的话才能让DP无后效性
显然我们可以发现f[i+1][j],f[i][j1]都是另一个人能得到的最大分数 又因为每次只能取牌顶或牌底,所以上述两个状态转移过来 又因为一个人的得分越大另一个人的得分越小 所以选择f[i+1][j],f[i][j1]中的较小值 那么一个人的得分知道另一个人依旧显然了。

【代码】

#include <cstdio>#include <cstring>#include <algorithm>const int size = 1000+10;bool v[size][size],s[size];int w[size][size],sum[size];int n,ans;int dfs(int,int);void print(int,int,int);int main() {    freopen("poker.in","r",stdin);    freopen("poker.out","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;i++) {        int x;scanf("%d",&x);        v[i][i]=true;w[i][i]=x;        sum[i]=sum[i-1]+x;    }    ans=dfs(1,n);    if(ans>sum[n]/2)        puts("WIN");    else puts("LOSE");    printf("%d\n",ans);    print(1,n,1);puts("");    return 0;}int dfs(int a,int b) {    if(v[a][b]==true) return w[a][b];    v[a][b]=true;w[a][b]=sum[b]-sum[a-1]-dfs(a+1,b);    int tmp=sum[b]-sum[a-1]-dfs(a,b-1);    w[a][b]=std::max(w[a][b],tmp);    return w[a][b];}void print(int a,int b,int x) {    if(a==b) return ;    if(sum[b]-sum[a-1]-w[a+1][b]==w[a][b]) {        if(x&1) printf("%d ",a);        print(a+1,b,x+1);    }    else {        if(x&1) printf("%d ",b);        print(a,b-1,x+1);    }}

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

【问题描述】

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

【输入】

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

【输出】

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

【输入输出样例】

Output.txt(样例输入)
5 3
1
3
2
Input.txt(样例输出)
1

【数据范围】

40%的数据,M200000
100%的数据,M,Di2.1109N105,保证答案大小在有符号 64 位整型变量
存储范围内。

【题解】

此题并不是dp(害人啊),分明就是简单的贪心啊啊啊~

要枚举的就是在第几个罪人的时候,从他开始一直到第N个罪人能够受到比较均衡的惩罚,也就是说,他少受的惩罚K=Sum[I,N]/(N+1I)刚刚好小于等于他应当承受的惩罚。
最后直接算序列的平方和

【代码】

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn=100001;int n;int w[maxn];long long m,sum,dev;long long ans;int main(){    cin>>m>>n;    for (int i=1;i<=n;i++) {scanf("%d",&w[i]);sum+=w[i];};    sort(w+1,w+n+1);    ans=2147483647;ans=ans*ans;ans=2*ans;    long long wrest=0;    for (int i=1;i<=n;i++)    {        if (wrest>ans) break;        long long use,rest,now;        rest=(sum-m)%(n-i+1);        use=(sum-m)/(n-i+1);        if (use<=w[i])         {           now=use*use*(n-i+1-rest)+(use+1)*(use+1)*rest+wrest;           if (now<ans) ans=now;        }        sum-=w[i];wrest+=((long long)w[i])*w[i];    }    cout<<ans;    return 0;}

总结

没什么好总结的,还需继续努力啊。。。

0 0