1.NOI 最大子矩阵

1:最大子矩阵

总时间限制: 
1000ms
 
内存限制: 
65536kB
描述
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵。

比如,如下4 * 4的矩阵

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2

的最大子矩阵是

9 2
-4 1
-1 8

这个子矩阵的大小是15。
输入
输入是一个N * N的矩阵。输入的第一行给出N (0 < N <= 100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在[-127, 127]。
输出
输出最大子矩阵的大小。
样例输入
4
0 -2 -7 0 9 2 -6 2
-4 1 -4  1 -1

8  0 -2
样例输出
15
来源
翻译自 Greater New York 2001 的试题
一般做法:N^3时间复杂度
/*方法:枚举每一行所有的区间情况,把这个区间从第一列向下加,加的过程中注意:因为有负数,所以如果之前加的和<0了,我们要舍弃之前的所有结果,重新开始新的矩阵,而且要注意对于ans的更新是在加的过程更新,因为随时有可能加上负数,虽然sum>0,所以加一次都要更新*/
#include<iostream>
using namespace std;
#include<cstdio>
const int N=101;
const int INF=1<<30;
int n,a[N][N];
int main()
{
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
      {
          scanf("%d",&x);
          a[i][j]=a[i][j-1]+x;
      }
    int ans=-INF;
    for(int i=1;i<=n;++i)
      for(int j=i;j<=n;++j)
      {
          int tmp=0;
          for(int k=1;k<=n;++k)
          {
              int num=a[k][j]-a[k][i-1];
              if(tmp>0) tmp+=num;
              else tmp=num;
              if(tmp>ans) ans=tmp;
          }
      }
      printf("%d\n",ans);
    return 0;
}
View Code

2.NOI 6047:分蛋糕

6047:分蛋糕

总时间限制: 
1000ms
 
内存限制: 
65536kB
描述

有一块矩形大蛋糕,长和宽分别是整数w 、h。现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形、且长和宽均为整数。切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕。请计算:最后得到的m块小蛋糕中,最大的那块蛋糕的面积下限。

假设w= 4, h= 4, m= 4,则下面的切法可使得其中最大蛋糕块的面积最小。

 

假设w= 4, h= 4, m= 3,则下面的切法会使得其中最大蛋糕块的面积最小:

 

 

输入
共有多行,每行表示一个测试案例。每行是三个用空格分开的整数w, h, m ,其中1 ≤ w, h, m ≤ 20 , m ≤ wh. 当 w = h = m = 0 时不需要处理,表示输入结束。
输出
每个测试案例的结果占一行,输出一个整数,表示最大蛋糕块的面积下限。
样例输入
4 4 4
4 4 3
0 0 0
样例输出
4
6
代码:
/*这个题和之前做的那个棋盘分割很像,但是又有些不同,棋盘分割要求确定每一个矩形。
而在这个题目中,只要矩形的长宽还有切割的次数确定了,无论这个面积的矩形位于大矩形的什么地方,结果都是相同的,那就没有必要固定矩形位置,仅仅固定矩形的大小就可以了
第一次做,我用的就是确定矩形的位置,开了一个五维数组,结果全部超时,虽然样例过了。 
f[i][j][k]表示的是i*j这个矩形分割为k份的最大面积的下限。
*/
#include<iostream>
using namespace std;
#include<cstdio>
#define N 21
int f[N][N][N];
int n,m,k;
#include<cstring>
void DP()
{
    /*第一次的超时做法*/
  /*for(int kk=1;kk<=k;++kk)
    for(int x1=0;x1<=n;++x1)
      for(int y1=0;y1<=m;++y1)
        for(int x2=x1+1;x2<=n;++x2)
          for(int y2=y1+1;y2<=m;++y2)
          {
              if(f[x1][y1][x2][y2][kk]<5000)
              continue;
              if(kk==1)
              {
                  f[x1][y1][x2][y2][kk]=(x2-x1)*(y2-y1);
              continue;
              }
            for(int x=x1+1;x<x2;++x)
            {
                f[x1][y1][x2][y2][kk]=min(max(f[x1][y1][x][y2][1],f[x][y1][x2][y2][kk-1]),f[x1][y1][x2][y2][kk]);
                f[x1][y1][x2][y2][kk]=min(max(f[x1][y1][x][y2][kk-1],f[x][y1][x2][y2][1]),f[x1][y1][x2][y2][kk]);
            }
            for(int y=y1+1;y<y2;++y)
            {
                f[x1][y1][x2][y2][kk]=min(max(f[x1][y1][x2][y][1],f[x1][y][x2][y2][kk-1]),f[x1][y1][x2][y2][kk]);
                f[x1][y1][x2][y2][kk]=min(max(f[x1][y1][x2][y][kk-1],f[x1][y][x2][y2][1]),f[x1][y1][x2][y2][kk]);
            }
          }*/
    
    for(int kk=1;kk<=k;++kk)
    for(int x1=1;x1<=n;++x1)
      for(int y1=1;y1<=m;++y1)
          {
              if(f[x1][y1][kk]<50000)/*记忆化搜索因为输入有多组数据,而且只要i,j,k确定,结果就不变,所以没有必要重复计算*/
              continue;
              if(kk==1)
              {
                  f[x1][y1][kk]=x1*y1;/*DP的初始化*/
              continue;
              }
            for(int x=1;x<x1;++x)
            {
            /*    f[x1][y1][kk]=min(max(f[x][y1][kk-1],(x1-x)*y1),f[x1][y1][kk]);*/
                /*f[x1][y1][kk]=min(max(x*y1,f[x1-x][y1][kk-1]),f[x1][y1][kk]);*/
                 for(int p=1;p<kk;p++)
                        f[x1][y1][kk]=min(f[x1][y1][kk],max(f[x][y1][p],f[x1-x][y1][kk-p]));
             /*这里与棋盘分割的不同,棋盘分割是上面的方程,因为棋盘分割要求“将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,”,所以必须是一块k-1份,一块1份才可以,
             而这个“分蛋糕”只要求蛋糕是矩形,没要求每切一份,剩下的也是矩形*/ 
            }
            for(int y=1;y<y1;++y)
            {
                /*f[x1][y1][kk]=min(max(f[x1][y][kk-1],x1*(y1-y)),f[x1][y1][kk]);*/
            /*    f[x1][y1][kk]=min(max(x1*y,f[x1][y1-y][kk-1]),f[x1][y1][kk]);*/
                for(int p=1;p<kk;p++)
                        f[x1][y1][kk]=min(f[x1][y1][kk],max(f[x1][y][p],f[x1][y1-y][kk-p]));
            }
          }
    
}
int main()
{
    memset(f,127,sizeof(f));
    while(scanf("%d%d%d",&n,&m,&k)==3&&n&&m&&k)
    {
        DP();
        printf("%d\n",f[n][m][k]);
    }
    return 0;
}
View Code

3. NOI  6049:买书

6049:买书

总时间限制: 
1000ms
 
内存限制: 
65536kB
描述

小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。

问小明有多少种买书方案?(每种书可购买多本)

输入
一个整数 n,代表总共钱数。(0 <= n <= 1000)
输出
一个整数,代表选择方案种数
样例输入
样例输入1:
20

样例输入2:
15

样例输入3:
0
样例输出
样例输出1:
2

样例输出2:
0

样例输出3:
0
代码:
/*变形版的“线段覆盖”,把餐馆向后扩展k长度,作为一个线段就可以了*/
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define N 101
int t,n,k,f[N];
struct Poi{
    int l,r,val;
//    bool operator <(const Poi &a)
//    const {    return r<a.r;}
};
Poi poi[N];
#include<algorithm>
void input()
{
    memset(f,0,sizeof(f));
    memset(poi,0,sizeof(poi));
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&poi[i].l);
        poi[i].r=poi[i].l+k;
    }
    for(int i=1;i<=n;++i)
    scanf("%d",&poi[i].val);
}
int ans=0;
void DP()
{
    
//    sort(poi+1,poi+n+1);
    for(int i=1;i<=n;++i)
    f[i]=poi[i].val;
    for(int i=2;i<=n;++i)
    {
        for(int j=1;j<i;++j)
        {
            if(poi[i].l>poi[j].r)
            f[i]=max(f[i],f[j]+poi[i].val);
        }
        
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        input();
        DP();
        ans=0;
        for(int i=1;i<=n;++i)
        ans=max(ans,f[i]);/*一开始犯了一个非常傻逼的错误,在DP中寻找f[i]的最大值,结果漏了f[1]恰好输入的数据有的就是仅仅建立第一个餐馆的利润最大,结果WA了*/
        printf("%d\n",ans);
    }
    return 0;
}
View Code

相关文章: