U280641 宠物小精灵之收服
输入格式
输入数据的第一行包含三个整数:N,M,K,分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。
之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。
输出格式
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。
f[i, j1, j2]
1. 集合 : 从 前 i 件物品中选, 一维体积不超过 j1 (精灵球)
二维体积不超过 j2 (体力),的所有选法的集合
w[i] = 1, 小精灵的价值看成 1
max(价值)
2. 计算集合
f[i, j1, j2] v1[i], v2[i]
第 i 件物品 选 或 不选
f[i, j1, j2] = max(f[i - 1, j1, j2], f[i - 1, j1 - v1i, j2 - v2i] + 1);
3. 边界
f[0, j1, j2] = 0
4. 时间: O(n^3)
#include <iostream>
using namespace std;
const int N = 1010, M = 510, K = 110;
int n, m, k;
int f[N][M];
int v1[K], v2[K];
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= k; i ++ ) cin >> v1[i] >> v2[i];
for (int i = 1; i <= k; i ++) // 物品数
for (int j1 = n; j1 >= v1[i]; j1 -- ) // 一维体积(精灵球)
for (int j2 = m; j2 >= v2[i]; j2 -- )
f[j1][j2] = max(f[j1][j2], f[j1 - v1[i]][j2 - v2[i]] + 1);
int res = -1, t; // res 代表max(价值), t 消耗的二维体积(体力)
for (int i = 0; i <= m - 1; i ++ )
if (res < f[n][i])
{
res = f[n][i];
t = i;
}
cout << res << ' ' << m - t << endl;
return 0;
}
U280643 摘花生
Hello Kitty想摘点花生送给她喜欢的米老鼠。
她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。
Hello Kitty只能向东或向南走,不能向西或向北走。
问Hello Kitty最多能够摘到多少颗花生。
每个点无非就是从它上面或者左边走过来,选择较大的一个就好。状态转移方程:f[i][j]=max(f[i-1][j],f[i][j-1])i-1,j即为从上面过来;i,j-1即为从左边过来。最后输出出口处即可。
最后输出一定要用printf才能ac,使用cout将无法ac。
cout和printf的区别
#include<iostream>
#include<algorithm>
using namespace std;
int dp[105][105],f[105][105];
int c,r,maxn;
int main()
{
int s;
cin>>s;
for(int i=0;i<s;i++)
{
cin>>c>>r;
for(int i=1;i<=c;i++)
for(int j=1;j<=r;j++)
{
cin>>dp[i][j];
f[i][j]=dp[i][j];
}
for(int i=1;i<=c;i++)
for(int j=1;j<=r;j++)
f[i][j]+=max(f[i-1][j],f[i][j-1]);
printf("%d\n",f[c][r]);
}
}
U280638 方格取数
设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:
某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。
输入格式
第一行为一个整数N,表示 N×N 的方格图。
接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
行和列编号从 1 开始。
一行“0 0 0”表示结束。
输出格式
输出一个整数,表示两条路径上取得的最大的和。
我们分两次从(1,1)到(n,n)分别求局部最优解,
但是这样不可取,因为我们第一次走完之后,产生的结果会对
第二次的取值产生影响,所以不能分开求。
那么我们就两次取数同时开始,定义数组dp[N][N][N],
dp[k][i1][i2]表示第一次的路径从(1,1)走到(i1,k-i1)以及第二次的路径从(1,1)走到(i2,k-i2)
所能取到的最大的数字和。
其中dp[k][i1][i2]表示:k=i1+i2,在k==n+n的时候就是到达了终点,
i1表示第一次取数走到了第i1行,根据k==i1+j1,j1=k-i1,说明第一次取数走到了(i1,j1)==>(i1,k-i1),
同理,i2表示第二次取数的路径到达了(i2,j2)==>(i2,k-i2)
一共有四种状态可以到大dp[k][i1][i2],所以我们在这四种情况中取max。
当i1=i2时,(i1,j1)==(i2,j2),而题目说当某次取走某个点的数之后,这个点就变成了0,所以当我们的路径重叠时,我们只能加一次这个点所以我们在循环里面要if判断。
#include<iostream>
using namespace std;
const int N=15;
int n;
int map[N][N];
int dp[2*N][N][N];
void qv(){
for(int k=2;k<=2*n;k++){
for(int i1=1;i1<=n;i1++){
for(int i2=1;i2<=n;i2++){
int j1=k-i1,j2=k-i2;
if(1<=i1&&i1<=n&&1<=i2&&i2<=n){
int &x=dp[k][i1][i2];
int t=map[i1][j1];
if(i1!=i2)t+=map[i2][j2];
x=max(x,dp[k-1][i1-1][i2-1]);
x=max(x,dp[k-1][i1-1][i2]);
x=max(x,dp[k-1][i1][i2-1]);
x=max(x,dp[k-1][i1][i2]);
x+=t;
}
}
}
}
cout<<dp[2*n][n][n]<<endl;
}
int main()
{
cin>>n;
int a,b,c;
while(cin>>a>>b>>c,a||b||c)map[a][b]=c;
qv();
return 0;
}