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; }
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; }
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; }