暑假补题记3
思路:一道dp,首先明确vis含义,vis[i-1][0]代表的是上一步是一个1的柱子地最优解,vis[i-1][1]代表的是上一个是一个2的柱子的最优解,然后就初始状态第一个题目是一定是0开始所以vis[0][1]="非常大的数" vis[0][0]=a+b,就是一个1的柱子加一个1的管道,然后开始遍历每一个节点,当他是一个路口的时候,就必须是要来到1所以 方程是vis[i][0]=inf,vis[i][1]=min(vis[i-1][0]+a*2+b*2,vis[i-1][1]+a+b*2);
前面那个就是不能在向0走,后面那个就是向1走,前一步在1柱子的时候向2走和前一个在2柱子的时候在向2走,然后求最小,
再下来就是不是路口的时候,可以向2也可以向1走求最小就可以。
然后结果就是因为最后一个一定是0所以是vis[n-1][0]+b;最后一根柱子n-1就是在最后一个到0的最优解
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> #define int long long #define inf 884888488848997 using namespace std; const int N=2e5+8; vector< pair<int,int> > q; int vis[N][2]; int32_t main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t; cin>>t; while (t--) { int n, a, b; cin >> n >> a >> b; string s; cin >> s; ::memset(vis,0,sizeof (vis)); vis[0][1] = inf, vis[0][0] = a + b; for(int i=1;i<s.size();i++) { int k=s[i]-'0'; if(k) { vis[i][0]=inf; vis[i][1]=min(vis[i-1][0]+2*a+2*b,vis[i-1][1]+a+2*b); } else { vis[i][0]=min(vis[i-1][0]+a+b,vis[i-1][1]+2*a+b*2); vis[i][1]=min(vis[i-1][0]+2*a+b,vis[i-1][1]+a+2*b); } } cout<<vis[n-1][0]+b<<endl; } return 0; }
题解:这题就是一个背包dp 主要是这个他有数量所以要考虑这个物品要几个的问题,直接在加一个循环来循环物品要多少个,
然后就是状态转移方程 dp[i][j]=max(dp[i][j],dp[i-1][j-k*s[i].w]+k*s[i].v);
其中有一个点就是前面不要的情况下用的是dp[i][j]而不是[i-1][j]因为他每一轮需要k循环就是有可能只要k个要不到全部,所以我们要判断要几个是最优解,
但是这样子就没办法继承上一个物品的最优解了,意思就是目前全部要的情况下,所以我们直接k==0开始遍历只要就可以继承上一个物品的最优解了,k==0是一个坑点
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define int long long using namespace std; const int N=300+5; int a[N]; int b[N]; int dp[N][N]; struct G{ int x; int w; int v; }s[N]; int32_t main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n,t; cin>>n>>t; for(int i=1;i<=n;i++) { cin>>s[i].x>>s[i].w>>s[i].v; } dp[0][0]=0; for(int i=1;i<=n;i++) { for(int j=1;j<=t;j++) { for(int k=0; k <= s[i].x && k*s[i].w <= j; k++) { dp[i][j]=max(dp[i][j],dp[i-1][j-k*s[i].w]+k*s[i].v); } } } cout<<dp[n][t]; return 0; }
背包dp,记录一下,方便回来复习。。。。。。
这个就是排个序,然后一个个用dp表示只要被前面的数表示出来了就直接pass掉就行了,
首先初始化dp[0]=1因为0不可能被表示出来所以标记上一,然后就是进入循环首先判断一下这个数是否被表示出来没有就加加,
然后用状态方程dp[j]+=dp[j-a[i]];这个的意思就是把一个数的倍数全部表示出来然后标记为一比如3出那么6-3是不是就标记掉6了然后9-3因为6已经被标记所以9就被标记了然后就是假设10标记,19-10==9然后9被标记了所以19也被标记了,这个就是10和3的组合被标记
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> const int INF = 0x3f3f3f3f; //#define int long long using namespace std; const int N=10000+5; int a[N]; int b[N]; int dp[105]; int32_t main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n); ::memset(dp,0, sizeof (dp)); dp[0]=1; int ans=0; for(int i=1;i<=n;i++) { if(!dp[a[i]])ans++; for(int j=a[i];j<=a[n];j++) { dp[j]+=dp[j-a[i]]; } } cout<<ans<<endl; } return 0; }
G-合并回文子串_第三周训练题单 (nowcoder.com)
一个区间dp,开个四维,想不到根本想不到
思路:首先我们先枚举串的长度,a枚举一个循环b枚举一个循环,然后在对a枚举该长度的左右端点,然后在对b枚举左右端点,这样4个循环然后就行初始化,也就是这两个枚举出的长度相加要是等于或者小于1那就给他标记上一,因为长度只要1的串必定是回文串,然后就是状态方程,假设如果dp[i][j]是回文的i(i和j代表左右端点),那么dp[i+1][j-1]也就必定是回文的,所以我们就反过来推,假设a[i]==a[j],然后dp[i+1][j-1]是回文串,那么dp[i][j]也必定是回文的,就用这种状态一个一个推出来即可,当然有4种状态需要考虑a的i和j~b的q和p~然后就是i and p~j and q, 就行啦最后统计最大。
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> const int INF = 0x3f3f3f3f; #define int long long using namespace std; const int N=60; char a[N],b[N]; int dp[N][N][N][N]; int32_t main() { //ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { scanf("%s\n%s",a+1,b+1); int ans=0; int n= strlen(a+1); int m=strlen(b+1); for(int l=0;l<=n;l++) for(int r=0;r<=m;r++) for(int i=1;i<=n-l+1;i++) { int j=i+l-1; for(int p=1;p<=m-r+1;p++) { int q=p+r-1; if(l+r<=1) dp[i][j][p][q]=1; else { dp[i][j][p][q]=0; if(a[i]==a[j]) dp[i][j][p][q] |= dp[i+1][j-1][p][q]; if(b[p]==b[q]) dp[i][j][p][q] |= dp[i][j][p+1][q-1]; if(a[i]==b[q]) dp[i][j][p][q] |= dp[i+1][j][p][q-1]; if(a[j]==b[p]) dp[i][j][p][q] |= dp[i][j-1][p+1][q]; } if(dp[i][j][p][q]) ans=max(ans,l+r); } } cout<<ans<<endl; } return 0; }
I-没有上司的舞会_第三周训练题单 (nowcoder.com)
还是dp,妈的一个树形dp搞半天
这个题就是去找那个子树的根节点最多然后删掉根节点就行,我们直接递归他
首先是存图,用一个单链表进行存图
void add(int x,int y) { e[idx] = y, en[idx] = h[x], h[x] = idx++; }
h表示头也就是父节点,en表示把他们连在一起的线段,e就表示谁被父节点连上
存完图,我们就找最初那个父节点,然后一个一个进行递归
然后就是状态方程了,首先对于一个点我们有两个选着一个是要一个是不要,
那么首先是不要,所以他的子节点我们就要或者不要,我们递归跑子节点(j就是子节点)的最优解也就是跑他要或者不要,跑出他的大小,就是dp[u][0]=max(dp[j][0],dp[j][1]);
两种状态子节点要或者不要取最大值。
然后就是要 dp[u][1]=dp[j][0];
那么你的子节点必定不要所以就是上面那个
然后一个回溯递归到最初那个父节点然后答案就是父节点要或者不要都最大值就结束
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define int long long using namespace std; const int N=10010; const int INF = 0x3f3f3f3f; const int mod=1e9; int a[N]; int h[N],e[N*2],en[N*2],idx; int dp[N][2]; bool vis[N]; void add(int x,int y) { e[idx] = y, en[idx] = h[x], h[x] = idx++; } void dfs(int u) { dp[u][1] = a[u]; for(int i = h[u]; i!=-1; i=en[i]) { int j=e[i]; dfs(j); dp[u][0] += max(dp[j][1],dp[j][0]); dp[u][1] += dp[j][0]; } } int32_t main() { //ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin>>n; ::memset(h,-1,sizeof (h)); for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=n-1;i++) { int l,k; cin>>l>>k; vis[l]= true; add(k,l); } int to = 1; while (vis[to]) to++; dfs(to); cout<<max(dp[to][1],dp[to][0]); return 0; }
题解: 首先你将每一个耐力值都记录一个最大值,就是这个耐力下你最大的力量
这里有一个坑点
如果一个英雄可以消灭掉i只那么i-1只i-2只都可以由他消灭,当然只要他有足够的力量,所以要从后往前去一个个推,把最大力量一直传递下去
for(int i=n-1;i>=1;i--) { sum[i]=max(sum[i],sum[i+1]); }
这样,在指定的耐力值的地方就有最大的力量去消灭,这样耐力为1的地方将出现最大的力量,力量一个递减的
然后就一个个去枚举这些怪物的力量,当一个怪物个大于当前耐力的力量就跳出,day++,然后在进去从下一个英雄开始跑。然后出现重复那么就是-1
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> #define int long long using namespace std; const int N=2e5+7; const int INF = 0x3f3f3f3f; const int mod=1e9; int d[N*2]; int h[N],e[N*2],ne[N*2],idx; int sum[N]; int a[N]; struct G { int p; int s; }k[N]; int32_t main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin>>n; int ma=0; for(int i = 1;i <= n; i++) { cin >> a[i]; ma=max(ma,a[i]); } int m; cin>>m; for(int i=1;i<=n;i++) { sum[i]=0; } int ma1=0; for(int i=1;i<=m;i++) { cin>>k[i].p>>k[i].s; sum[k[i].s]=max(k[i].p,sum[k[i].s]); ma1=max(ma1,k[i].p); } for(int i=n-1;i>=1;i--) { sum[i]=max(sum[i],sum[i+1]); } int ans = 0, tiao = 1; int f=1; while (tiao <= n) { ans++; int i = tiao; int x = 0; while (1) { x = max(x, a[i]); if(sum[i-tiao+1]<x) break; i++; } if(tiao==i) { f=0; break; } tiao=i;//最多可以杀多少只 } if(f) cout << ans << endl; else cout<<-1<<endl; } return 0; }