1.NOI 最大子矩阵
4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2
15
/*方法:枚举每一行所有的区间情况,把这个区间从第一列向下加,加的过程中注意:因为有负数,所以如果之前加的和<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:分蛋糕
有一块矩形大蛋糕,长和宽分别是整数w 、h。现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形、且长和宽均为整数。切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕。请计算:最后得到的m块小蛋糕中,最大的那块蛋糕的面积下限。
假设w= 4, h= 4, m= 4,则下面的切法可使得其中最大蛋糕块的面积最小。
假设w= 4, h= 4, m= 3,则下面的切法会使得其中最大蛋糕块的面积最小:
4 4 4 4 4 3 0 0 0
/*这个题和之前做的那个棋盘分割很像,但是又有些不同,棋盘分割要求确定每一个矩形。 而在这个题目中,只要矩形的长宽还有切割的次数确定了,无论这个面积的矩形位于大矩形的什么地方,结果都是相同的,那就没有必要固定矩形位置,仅仅固定矩形的大小就可以了 第一次做,我用的就是确定矩形的位置,开了一个五维数组,结果全部超时,虽然样例过了。 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:买书
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
样例输入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; }
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。
一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。
我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。
小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。
现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?
样例输入1: 10 100 5 7 10 2 40 2 50 1 20 4 20 样例输入2: 10 100 5 8 110 12 10 20 10 5 200 1 110
样例输出1: 3 30 样例输出2: 0 100
/*一个普通的二维01背包,注意体力不能是0,就可以了, f[j][k]表示在使用了j个球,收到k点伤害的最大捕捉数目。 ans最好DP中更新,因为DP结束以后,最优值可能存在很多位置,伤害和球的数目不一定恰好用完 */ #include<iostream> using namespace std; #include<cstdio> #define N 1001 #define M 501 int n,m,k; struct Jl{ int qiu,dam; }; Jl jl[101]; struct Ans{ int sum,tl; }; Ans ans; int f[N][M]; void input() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;++i) { scanf("%d%d",&jl[i].qiu,&jl[i].dam); } } void DP() { ans.sum=0; ans.tl=m; for(int i=1;i<=k;++i) for(int j=n;j>=jl[i].qiu;--j) for(int k=m-1;k>=jl[i].dam;--k) { f[j][k]=max(f[j][k],f[j-jl[i].qiu][k-jl[i].dam]+1); if((f[j][k]>ans.sum)||(f[j][k]==ans.sum&&(m-k)>ans.tl)) { ans.sum=f[j][k]; ans.tl=m-k; } } } int main() { input(); DP(); printf("%d %d\n",ans.sum,ans.tl); return 0; }
原文:http://www.cnblogs.com/c1299401227/p/5371636.html