题意:
给定 数组 an:全为1;
给与数组 bn, cn; 将an变为bn后 得到cn点贡献;
你一共有k次操作 求得到最大贡献值;
从 1 中得到数字 i 的最小操作数。要做到这一点,使用 动态规划就足够了
题解:
现在问题本身可以简化为一个背包问题:有 n 个物品,第 i 个物品的重量为 di 和成本 ci,
你必须找到总重量不超过最大成本 k 的一组物品。
代码:
#include <iostream>
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define CAS int cas;cin>>cas;while(cas--)
#define mem(a,b) memset(a,b,sizeof(a))
#define f(i,l,h) for(int i=l;i<=h;++i)
#define eps 1e-9
#define pi acos(-1.0)
#define pb push_back
#define se second
#define fi first
const int maxn=500005;
const int INF = 1005;
//typedef pair<int, int> pii;
typedef long long ll;
using namespace std;
struct node{
int d; // 数组c的值
int cnt;// d值的操作数
};
int main()
{
int t;
cin>>t;
while(t--){
int n,k;
int b[1005],a[1005*2];
node v[1005];
cin>>n>>k;
vector<int>dp(k+10);
for(int i=0;i<n;i++)
cin>>b[i];
for(int i=1;i<=1002;i++) a[i]=INF;
a[1]=0;a[2]=1,a[3]=2,a[4]=2;
for(int i=2;i<=1002;i++){ //初始化数组a存储 由1变为i最小操作数;
for(int j=1;j<=i;j++){
int m=i/j;
a[i+m]=min(a[i+m],a[i]+1);
}
}
for(int i=0;i<n;i++){
cin>>v[i].d;
v[i].cnt=a[b[i]];
}
// 背包问题 滚动数组
for(int i=0;i<n;i++){
for(int j=k;j>=v[i].cnt;j--){
dp[j]=max(dp[j],dp[j-v[i].cnt]+v[i].d);
}
}
cout<<dp[k]<<endl;
}
return 0;
}
https://www.acwing.com/problem/content/3665/
题意:
给定一个长度为 n 的整数序列 a1,a2,…,an。
请你选出一个该序列的严格上升子序列,要求所选子序列的各元素之和尽可能大。
题解:
先考虑暴力DP:设 f[i] 表示在 a1∼ai 中,且以 ai 结尾的所有上升子序列中,元素和的最大值。
有转移方程:
f[i]=ai+Maxf[j](0≤j<i,aj<ai)
将序列 a 离散化,考虑优化对 fi 的转移。
设 g(x) 表示所有 j<i 且 aj=x 的 fj 的最大值,那么 Maxf[j] (0≤j<i,aj<ai) 就等于 Max g(x) (x<ai),
注意到这项是 g 的一个前缀最大值,这恰可以用树状数组动态维护。
具体可见代码。
代码:
#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
typedef long long ll; // 本题中 a 的上线是 10 ^ 9
const int N = 100005;
int n, a[N], diff[N], sz; // 离散化
// 树状数组
ll c[N], res;
int lowbit(int x){
return x&(-x);
}
void add(int x, ll k) {
while(x<=sz){
c[x] = max(c[x], k);
x+=lowbit(x);
}
}
ll qry(int x) {
ll res = 0;
for (; x>=1; x -= lowbit(x)) res = max(res, c[x]);
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
cin>>a[i];
diff[i] = a[i];
}
sort(diff+1, diff+1+ n);
sz = unique(diff+1, diff + n+1) - diff-1;
for (int i = 1; i <= n; ++i) {
int k = lower_bound(diff+1, diff+1 + sz, a[i]) - diff; // 求出离散化之后的值
ll f = diff[k] + qry(k-1); // 求出 f[k]。 f[k]=ak+Max f[k-1]
res = max(res, f), add(k, f); // 更新答案并将 f[k] 存到树状数组中 k 的位置。
}
printf("%lld\n", res);
return 0;
}
https://www.acwing.com/problem/content/3763/
题意:
n个点 每个点课加wi油
n-1条路 每条路耗油c;
求某条路线最大剩油量 (也可不走 )
题解:
树型DP:
我们期望通过枚举路径来找出最优路径,通过枚举路径最高点(在整个树形结构里)找到以该点为最高点的路径。
而该点路径长度为该点油量加上以其两个孩子结点分别为路径最高点的路径长度,这是原问题的一个子问题,可以使用树型DP。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
#define ll long long
typedef long long LL;
const int N = 3e5 + 5;
typedef pair<int, int> PII;
int n, w[N];
LL ans = -9e18, f[N];
vector<PII> g[N];
void chmax(ll &x,ll y){
if(x<=y) x=y;
}
void dfs(int u, int fa) {
f[u]=w[u];
chmax(ans,w[u]);
for(int i=0;i<g[u].size();i++){
int v=g[u][i].first; int t=g[u][i].second;
if(v==fa) continue;
dfs(v,u);
chmax(ans,f[v]+f[u]-t);//更新ans
chmax(f[u],f[v]+w[u]-t);//更新f[u] f[u]= 节点v的f + 节点u的值 + 减去uv边的耗油量
}
}
int main() {
cin>>n;
for(int i=1;i<=n;i++ ) cin>>w[i];
for(int i=0;i<n-1;i++){
int u,v,k;
cin>>u>>v>>k;
g[u].emplace_back(v,k);//u->v 这条路花费k
g[v].emplace_back(u,k);
}
dfs(1,0); //从节点1 父节点0 dfs
// cout<<d[1]
cout<<ans<<endl;
}
/*
5
6 3 7 5 10
1 2 10
1 3 6
2 4 1
1 5 1
*/
思路:
二分答案,答案要求的就是修改序列的最小长度
slove的思路是:
依次枚举长度k的进行修改的区间(也就是题目中的修改成本),
对于每一个区间,求出除去修改区间以外的序列还需要多少dx,dy来到达目的地(x,y)。
对于dx,dy, 首先需要满足 len >= abs(dx)+abs(dy) ,
其次如果有冗余的长度,即 len>abs(dx)+abs(dy) 的情况下,我们需要用两个相反的指令来抵消,
所以还需要满足 (len-abs(dx)-abs(dy))%2==0,
另外答案为0和-1的时候先判断一下。
代码:
#include <iostream>
const int maxn=2e5+10;
using namespace std;
int x[maxn],y[maxn];
string s;
int res,n,a,b;
int slove(int k){
// cout<<k<<endl;
for(int i=1;i+k-1<=n;i++){
int sum1=x[i-1]+x[n]-x[i+k-1];
int sum2=y[i-1]+y[n]-y[i+k-1];
int ans1=b-sum2;
int ans2=a-sum1;
int ans=abs(ans1)+abs(ans2);
//cout<<ans<<endl;
if(ans<=k) return 0;
}
return 1;
}
int main()
{
cin>>n;
cin>>s;
s=" "+s;
for(int i=1;i<=n;i++){
x[i]=x[i-1];y[i]=y[i-1];
if(s[i]=='R') x[i]++;
else if(s[i]=='L') x[i]--;
else if(s[i]=='U') y[i]++;
else y[i]--;
}
cin>>a>>b;
res=abs(a)+abs(b);
if(res>n||n%2!=(res)%2){
cout<<-1<<endl;
return 0;
}
int l=0,r=n;
while(l<r){
int mid=(l+r)/2;
if(slove(mid)) l=mid+1;
else r=mid;
}
cout<<l<<endl;
return 0;
}
https://codeforces.com/contest/1634/problem/D
题解:
使用map<int,int>mp 统计差值次数
先指定第1 2两个数不变,n-2次询问其他数,得到一个最大值f。
若mp.size()==1 flag=1;
初始fg=2(第二个数)
清空mp,继续统计差值
根据这个最大值的下标,再n-2次去找最大值fg
1.若flag=1&&fg=2
输出1 2
2.若fg!=2
输出f fg
3.若fg==2
3.1 若 mp.size()==1 输出1 f
3.2 反之 输出 2 f;
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
map<int,int> mp;
mp.clear();
int n;
int mk=-1,f,flag=0;
cin>>n;
for(int i=3;i<=n;i++){
cout<<"? 1"<<" 2 "<<i<<endl;
fflush(stdout);cout.flush();
int x;
cin>>x;
mp[x]++;
if(mk<x)
mk=x,f=i;
}
if(mp.size()==1)
flag=1;
int fg=2,mk1=mk;
mp.clear();
for(int i=2;i<=n;i++){
if(i==f) continue;
cout<<"? 1 "<<f<<" "<<i<<endl;
fflush(stdout);cout.flush();
int x;
cin>>x;
mp[x]++;
if(x>mk1)
mk1=x,fg=i;
}
if(flag&&fg==2){
cout<<"! 1 "<<"2"<<endl;
fflush(stdout);
continue;
}
if(fg!=2){
cout<<"! "<<f<<" "<<fg<<endl;
fflush(stdout);cout.flush();
continue;
}else{
if(mp.size()==1)
cout<<"! 1 "<<f<<endl;
else
cout<<"! 2 "<<f<<endl;
}
fflush(stdout);cout.flush();
}
return 0;
}