A 机器人军团
典型的最长上升子序列问题,只不过这里是不下降子序列,与前者只有一个符号的区别
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include<queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
using namespace std;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 33;
inline ll nextLong()
{
ll tmp = 0, si = 1;
char c;
c = getchar();
while (c > '9' || c < '0')
{
if (c == '-')
si = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
tmp = tmp * 10 + c - '0';
c = getchar();
}
return si * tmp;
}
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll datas[500000], lower[500000];
int DETERMINATION()
{
ll n;
n = nextLong();
for (int i = 1; i <= n; i++)
datas[i] = nextLong();
fill(lower, lower + n + 1, INF);
lower[1] = datas[1];
ll length = 1;
for (int i = 2; i <= n; i++)
{
if (datas[i] >= lower[length])
lower[++length] = datas[i];
else
{
ll location = lower_bound(lower + 1, lower + 1 + length, datas[i]) - lower;
lower[location] = datas[i];
}
}
println(length);
return 0;
}
B 抄近路
这个题给定的是格子数,但是按照题意需要在格点上枚举状态,这里的格点会比格子多出一行一列,所以要多枚举一点。题目中给了一个反二维数列的坐标系,但是无所谓,因为这个格子地图是中心对称的,按正常二维数组下的坐标系(反Y轴第四象限)即可。(走格子对角线一定比走两条边的路程要短,这个无需判断)
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include<queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
using namespace std;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 33;
inline ll nextLong()
{
ll tmp = 0, si = 1;
char c;
c = getchar();
while (c > '9' || c < '0')
{
if (c == '-')
si = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
tmp = tmp * 10 + c - '0';
c = getchar();
}
return si * tmp;
}
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll diagram[1010][1010];
const double slope = 100*sqrt(2);
ld dp[1010][1010];
int DETERMINATION()
{
ll n, m;
n = nextLong(), m = nextLong();
ll k;
k = nextLong();
for (int i = 0; i <= n+1; i++)
for (int j = 0; j <= m+1; j++)
dp[i][j] = INF;
for (int i = 1; i <= k; i++)
{
ll tmp1, tmp2;
tmp1 = nextLong(), tmp2 = nextLong();
diagram[tmp1+1][tmp2+1] = 1;
}
/*for (int i =n+1;i>=2; i--)
{
for (int j = 2; j <= m+1; j++)
{
cout << diagram[i][j] << " ";
}
cout << endl;
}*/
dp[1][1] = 0;
for (int i = 1; i <=n+1; i++)
{
for (int j = 1; j <= m+1; j++)
{
if (i ==1 && j == 1)
continue;
if (!diagram[i][j])
dp[i][j] = min(dp[i-1][j] + 100, dp[i][j - 1]+100);
else
{
//cout << i << " " << j << endl;
dp[i][j] = min(dp[i-1][j - 1] + slope, dp[i][j]);
}
}
}
//for (int i = 1; i <= n + 1; i++)
//{
// for (int j = 1; j <= m+1; j++)
// cout << dp[i][j] << " ";
// cout << endl;
//}
cout << int(dp[n + 1][m + 1] + 0.5) << endl;
return 0;
}
C 魔法矿石
这是一个典型的DAG问题,直接建图进行记忆化搜索即可,值得注意的是要把所有矿石当做起点遍历一次。
(输入格式比较特殊,故使用java)
import java.util.*;
import java.io.*;
import java.math.*;
import java.text.*;
public class Main
{
static boolean connection[][]=new boolean[1200][1200];
static int dp[]=new int[1200];
static int mine[]=new int[1200];
static int n;
public static int dfs(int current)
{
if(dp[current]>0)
return dp[current];
else
{
dp[current]=mine[current];
for(int i=current;i<=n;i++)
{
if(connection[current][i])
{
dp[current]=Math.max(dp[current],dfs(i)+mine[current]);
}
}
return dp[current];
}
}
public static void main(String args[])throws IOException
{
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
String tmp=bf.readLine();
n=Integer.parseInt(tmp);
tmp=bf.readLine();
String tmps2[]=tmp.trim().split(" ");
for(int i=1;i<=n;i++)
mine[i]=Integer.parseInt(tmps2[i-1]);
for(int i=1;i<=n;i++)
{
tmp=bf.readLine();
String tmps[]=tmp.trim().split(" ");
int st=Integer.parseInt(tmps[0]);
for(int j=1;j<tmps.length;j++)
connection[st][Integer.parseInt(tmps[j])]=true;
}
int ans=0;
for(int i=1;i<=n;i++)
ans=Math.max(ans,dfs(i));
System.out.println(ans);
bf.close();
}
}
E 友好城市
因为航线不能有交叉,所以可以假设按距离排序后某一南岸城市(以for遍历,保证递增)对应的北岸城市的位置在水平方向是递增的。所以这就变成了一个最长上升子序列的问题。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 10000000;
const int tool_const = 19991126;
const int tool_const2 = 33;
//template<typename T>
//inline BigInteger nextLong()
//{
// BigInteger tmp = 0, si = 1;
// char c;
// c = getchar();
// while (!isdigit(c))
// {
// if (c == '-')
// si = -1;
// c = getchar();
// }
// while (isdigit(c))
// {
// tmp = tmp * 10 + c - '0';
// c = getchar();
// }
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {
// x = -x;
// putchar('-');
// }
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
//}
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
struct node
{
ll from, to;
}nodes[50000];
bool cmp(node a, node b)
{
return a.to < b.to;
}
ll lower[510000];
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll x, y;
cin >> x >> y;
ll n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> nodes[i].from >> nodes[i].to;
sort(nodes + 1, nodes + 1 + n, cmp);
ll ans = 1;
lower[1] = nodes[1].from;
for (int i = 2; i <= n; i++)
{
if (lower[ans] < nodes[i].from)
lower[++ans] = nodes[i].from;
else
{
ll loc = lower_bound(lower+1, lower+ans, nodes[i].from)-lower;
lower[loc] = nodes[i].from;
}
}
cout << ans << endl;
return 0;
}
F 合唱队列
枚举C位所在的位置,然后在C位左边求最长上升子序列,在右边求最长下降子序列。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 10000000;
const int tool_const = 19991126;
const int tool_const2 = 33;
//template<typename T>
//inline BigInteger nextLong()
//{
// BigInteger tmp = 0, si = 1;
// char c;
// c = getchar();
// while (!isdigit(c))
// {
// if (c == '-')
// si = -1;
// c = getchar();
// }
// while (isdigit(c))
// {
// tmp = tmp * 10 + c - '0';
// c = getchar();
// }
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {
// x = -x;
// putchar('-');
// }
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
//}
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll ar[9855];
ll dp[9855];
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll n;
cin >> n;
ll mx = -1, keyloc = 0;
for (int i = 1; i <= n; i++)
cin >> ar[i];
ll ans1 = 0, ans2 = 0,finans=9999999;
for (int keyloc = 1; keyloc <= n; keyloc++)
{
ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; i++)
dp[i] = 1;
for (int i = 2; i <= keyloc; i++)
{
for (int j = 1; j < i; j++)
if (ar[j] < ar[i])
dp[i] = max(dp[i], dp[j] + 1);
}
for (int i = 1; i <= keyloc; i++)
ans1 = max(ans1, dp[i]);
for (int i = 1; i <= n; i++)
dp[i] = 1;
for (int i = keyloc + 2; i <= n; i++)
{
for (int j = keyloc+1; j < i; j++)
if (ar[j] > ar[i])
dp[i] = max(dp[i], dp[j] + 1);
}
for (int i = keyloc +2; i <= n; i++)
ans2 = max(ans2, dp[i]);
//cout << ans1 << " " << ans2 << endl;
finans = min(n - (ans1 + ans2), finans);
}
cout <<finans<< endl;
return 0;
}
G 简单背包问题
这题本应该有spj的,但是没有。直接dfs即可
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextLong()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll dp[199][1990],ar[199];
bool vis[199];
bool dfs(ll current, ll target)
{
if (target == 0)
return true;
else if (target < 0)
return false;
else
{
if (current < 1)
return false;
if (dfs(current - 1, target - ar[current]) == true)//注意01背包的两种状态
{
cout << ar[current] << " ";
return true;
}
else
dfs(current - 1, target);
}
}
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll s, n;
cin >> s >> n;
for (int i = 1; i <= n; i++)
cin >> ar[i];
if (dfs(n,s) == true);
else
cout << "Failed!" << endl;
return 0;
}
H 01背包模板题
记忆化搜索/递推式皆可,但是数据大了就只能用滚动数组优化的递推
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextLong()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll dp[2300][2300];
struct data2
{
ll w, v;
}datas[99999];
bool vis[2300][2300];
ll dfs(ll current, ll remaining,ll limit)
{
ll &tmp = dp[current][remaining];
if (tmp)
return tmp;
else if (current > limit)
return dp[current][remaining] = 0;
else
{
ll ans1 = dfs(current + 1, remaining, limit);
ll ans2 = -INF;
if (remaining >= datas[current].w)
ans2 = dfs(current + 1, remaining - datas[current].w, limit)+datas[current].v;
return dp[current][remaining]=max(ans1,ans2);
}
}
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll m, n;
cin >> m >> n;
for (int i = 1; i <= n; i++)
cin >> datas[i].w >> datas[i].v;
cout << dfs(0, m, n) << endl;
return 0;
}
I 超级教主
对本题的分析应该从状态转移方程入手。
首先假设状态为吃掉某个能量球之后剩下的能量,于是对于每个能量球而言,就有两种情况:直接吃掉这个能量球及以下的能量球,或者吃完下边某个能量球之后再吃当前这个能量球。
于是显然就有状态转移方程dp[i]=max(dp[i],dp[j]+(sum[i]-sum[j])-100*i)。这个方程的sum[i]和100i,sum[j]都是随着i或j的变化而单调递增的,所以可以把方程后半部分看作(dp[j]-sum[j])+(sum[i]-100*i),i可以由一个循环遍历,而dp[j]和sum[j]就需要用一个单调队列存储一个整体的最大值。
#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%d", &a)
#define println(a) printf("%d\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
int dp[2000020];
int sum[2000020],a[2090020];
int queues[2090020];
int DETERMINATION()
{
//ios::sync_with_stdio(false);
//cin.tie(0), cout.tie(0);
int n,init;
lldin(n), lldin(init);
for (int i = 1; i <= n; i++)
lldin(a[i]);
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
int pt1 = 1,pt2 = 1;
dp[0] = init;//初始化
for (int i = 1; i <= n; i++)
{
while (pt2 > pt1&&(dp[queues[pt1+1]] - i*100) < 0)//如果说这个点压根跳不上去
pt1++;
while (pt2 > pt1&&(dp[queues[pt2]]-queues[queues[pt2]]) < (dp[i-1]-sum[i-1]))
pt2--;
queues[++pt2] = i - 1;
dp[i] = max(dp[i], (dp[queues[pt1+1]] - sum[queues[pt1+1]]) + sum[i] - 100 * i);
//println(dp[i]);
}
println(dp[n]);
return 0;
}
简单背包问题||
这是01背包模板题,记忆化搜索与递推皆可
import java.io.*;
import java.util.*;
import java.math.*;
import java.text.*;
public class Main
{
static int dp[][]=new int[34][20090];
public static int dfs(int current,int v,int limit,int inventory[])
{
if(dp[current][v]>0)
return dp[current][v];
else if(current>limit)
return dp[current][v]=0;
else
{
int ans1=0,ans2=0;
if(v>=inventory[current])
ans1=dfs(current+1, v-inventory[current], limit, inventory)+inventory[current];
ans2=dfs(current+1,v,limit,inventory);
return dp[current][v]=Math.max(ans1,ans2);
}
}
public static void main(String args[])throws IOException
{
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
st.nextToken();int v=(int)st.nval;
st.nextToken();int n=(int)st.nval;
int ar[]=new int[n+122];
for(int i=1;i<=n;i++)
{
st.nextToken();ar[i]=(int)st.nval;
}
pw.println(v-dfs(1, v,n, ar));
pw.flush();
pw.close();
}
}
货币系统问题
这类似于走楼梯问题的解法。对于每个面值来说,它可以继承(当前面值-某种货币)面值的凑法数,如此处理到N就是答案
import java.io.*;
import java.util.*;
import java.math.*;
import java.text.*;
public class Main
{
static int dp[]=new int[1599];
static int arr[]=new int[1599];
public static void main(String args[])throws IOException
{
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int v,n;
st.nextToken();n=(int)st.nval;
st.nextToken();v=(int)st.nval;
for(int i=1;i<=n;i++)
{
st.nextToken();arr[i]=(int)st.nval;
}
dp[0]=1;
for(int i=1;i<=n;i++)
for(int j=arr[i];j<=v;j++)
{
dp[j]+=dp[j-arr[i]];
}
pw.println(dp[v]);
pw.flush();
pw.close();
}
}
货币面值
由于每种货币只能用一次,所以可以看做一个01背包,数组下标对应所求金额,值的正负决定是否有方案。如果容量为i的时候对应的值大于0(初始化为负值),那么就是有凑数方案的,反之没有,找出最小值即可。
数字分组I
这可以看做一个01背包。其中最大容量是所有数总和的一半,找出这样一个最大值之后与总和作差即可。
数字分组II
此题并没有什么很明显的规律,但是数据范围很小,所以直接用状态压缩枚举即可。1到2^n-1的二进制表达式就可以代表分组方式,记录合法差值的最大值即可。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll inventories[50];
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll n;
cin >> n;
ll mx = 0, sum = 0;
for (int i = 1; i <= n; i++)
{
cin >> inventories[i];
sum += inventories[i];
mx = max(mx, inventories[i]);
}
ll ans = 0;
for (int i = 1; i <(1<<n); i++)
{
//cout << bitset<10>(i) << endl;
ll tmpsum = 0, tmpans = 0;
for (int j = 1; j <= n; j++)
{
if (((i >> j) & 1) > 0)
tmpsum += inventories[j];
}
tmpans = abs(tmpsum-abs(sum - tmpsum));
if (tmpans <= mx)
ans = max(tmpans, ans);
}
cout << ans << endl;
return 0;
}
完全背包问题
这是完全背包的模板题,递推即可。不建议使用记忆化搜索,因为很可能内存超限。
收益
这是一个比较奇特的完全背包问题。因为它在模板的基础上多添加了一次循环,也就是题目中所谓天数的循环。在每一天内,可以看出目前已有的资金与债券间构成了一个完全背包的关系,对每一天的收益进行求解,并把收益加到总资金里,如此进行操作到天数为n即可
值得一说的是,由于题目所说这些价格和本金都是1000的倍数,那么就可以把这些数都除以1000以简化对背包空间的枚举。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll dp[250000];
pair<ll, ll>stocks[50000];
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t;
cin >> t;
while (t--)
{
ll initfund, n;
cin >> initfund >> n;
ll d;
cin >> d;
for (int i = 1; i <= d; i++)
{
cin >> stocks[i].first >> stocks[i].second;
stocks[i].first/= 1000;
}
ll capital;
for (int i = 1; i <= n; i++)
{
capital = initfund / 1000;//现有资本
reset(dp, 0);//清空这个滚动数组
for (int j = 1; j <= d; j++)
for (int k = stocks[j].first; k <= capital; k++)
dp[k] = max(dp[k - stocks[j].first] + stocks[j].second, dp[k]);
initfund += dp[capital];//本金+收益
}
cout << initfund << endl;
}
return 0;
}
多重背包
这是多重背包的模板题,也是综合了三种背包问题的集大成者。这个模板的主要内容可以由如下流程表述
- 第一步是构建最外层的物品序号循环
- 第二步是判断当前物品是否超出或等于背包的容量,如果是,就进行完全背包的操作(能装多少装多少)
- 第三步是进行状态压缩优化的二进制枚举的多重背包的操作,这样的优化有一次装载多个物品的效果,可以优化时间
- 第四步是检查是否还漏下一些物品没有装,如果是,就进行01背包,直接把剩余物品乘以余下的常数当做整体放入即可。
说来说去这个用滚动数组优化的动态规划数组只有一维,而针对于某一个物品就进行了上述如此多的操作,它是怎么保证答案正确性的呢?
滚动数组的优化是由于某个状态转移方程的一个维度在任意状态时只与相邻状态有关,故而省略这一个维度所存储的大部分数据所做的优化。
如何理解呢?比如说01背包问题的状态转移方程是dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]),也就是说每个i只和i-1有关,和i-2什么的统统没有关系(这也是动态规划状态上的无后效性决定的),而且你还不想保存过去数据的话,就可以直接用0和1代替第一维,即只有当前物品和前一个物品这两个状态,在这两个状态间迭代就可以得到最终答案。
实际上,也完全可以省掉这一个维度,直接用一维数组进行计算也是可以的,但是这个就需要确定好滚动方向,以免造成数据未更新或者一些不必要的覆盖。
比如滚动数组优化后的01背包状态转移方程dp[j]=max(dp[j],dp[j-w[i]]+v[i]),如果你正向枚举j的话,就会出现一个尴尬的事情:当你枚举到某个位置的时候,你会发现dp[j-w[i]]保存的是dp[i][j-w[i]]而不是dp[i-1][j-w[i]],因为前者你已经在正向循环中计算过了,所以原本的状态数据被覆盖掉,就会导致错误的答案(这是完全背包的解法而不是01背包的解法)。所以就需要采取逆向循环枚举。
比如一组数据:70 3(容量 物品数量)
71 100,69 1,1 2,(体积和价值)
- 当你分析第一个物品时,你会发现这个无法装入背包,故动态规划数组内所有值仍然是0
- 当你分析第二个物品时,这个物品可以装入背包,逆向循环后只有dp[69]和dp[70]的值都是1
- 当你分析第三个物品时,这个物品可以装入背包,逆向循环时,dp[70] 保存的实际上是dp[2][70]的值,同样dp[69]也是dp[2][69]的值,而dp[3][70]会由dp[2][69]和dp[2][70]转移而来,之前的数据还未覆盖,可以用来计算。答案为3
#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
using namespace std;
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
struct inventory
{
ll amount, volume, value;
}inventories[599997];
ll dp[599997];
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, v;
cin >> v>> n;
for (int i = 1; i <= n; i++)
cin >> inventories[i].volume >> inventories[i].value >> inventories[i].amount;
for (int i = 1; i <= n; i++)
{
if (inventories[i].amount*inventories[i].volume >= v)
{
for (int j = inventories[i].volume; j <= v; j++)
dp[j] = max(dp[j], dp[j - inventories[i].volume] + inventories[i].value);
}
else
{
ll tmpcnt = inventories[i].amount;
for (int k = 1; k <= tmpcnt; k <<= 1)
{
for (int j = v; j >= inventories[i].volume*k; j--)
dp[j] = max(dp[j], dp[j - inventories[i].volume*k] + inventories[i].value*k);
tmpcnt -= k;
}
if (tmpcnt)
{
for (int j = v; j >= inventories[i].volume * tmpcnt; j--)
dp[j] = max(dp[j], dp[j - inventories[i].volume* tmpcnt] + inventories[i].value*tmpcnt);
}
}
}
cout << dp[v] << endl;
return 0;
}