前言:一直想练练dp,正好衣神弄了个训练赛。。上次看cgold大佬的题解心血来潮所以自己试着写了第一次题解。。可惜本蒟蒻的能力太差有两道题做不太出,只好搬运学习其它大佬的题解了。。
a题
https://vjudge.net/contest/355951#problem/A
这题做题的过程十分痛苦
我又双叒叕看错题意了。。
以为是必须在对角线上
其实是随便n*n的都行。。
大概思路是从一个角开始更新,统计左边和上边相同的长度
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; char a[1005][1005]; int dp[1005][1005]; int n; int main() { int n, ans; while (cin>>n , n) { ans = 1; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { cin >> a[j][i]; } for (int i = 0; i < n; i++) { for (int j = n - 1; j >= 0; j--) { dp[i][j] = 1; if (i == 0 || j == n - 1) continue; int q = dp[i - 1][j + 1]; for (int k = 1; k <= q; k++) { if (a[i - k][j] == a[i][j + k]) dp[i][j]++; else break; } ans = max(ans, dp[i][j]); } } printf("%d ", ans); } return 0; }
b题
https://vjudge.net/contest/355951#problem/B
这题有点难。。
蒟蒻直接下线。。。
那就学下网上大佬的方法吧
用dp(j, k)表示,取j 个候选人,使其辩控差为k 的所有方案中,辩控和最大的那个方案(该方案称为“方案dp(j, k)”)的辩控和
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <vector> #include <algorithm> #include <string> #include <map> using namespace std; #define N 220 #define met(a, b) memset(a, b, sizeof(a)) #define INF 0xffffff const long long Max = 2000000000; typedef long long LL; int D[N], P[N]; int dp[30][1000]; ///dp[i][k]代表选 i 个人, 辩控差为 k 的辩控和最大 int pre[30][1000]; ///pre[i][k] 存的是它是上次选的人 int Answer[N]; ///记录选中的这 m 个人的编号 int main() { int n, m, iCase=1; while(scanf("%d%d", &n, &m), n||m) { int i, j, k, Min, t1, t2; met(D, 0); met(P, 0); met(dp, -1); met(pre, 0); met(Answer, 0); for(i=1; i<=n; i++) scanf("%d%d", &D[i], &P[i]); Min = m*20; ///它的辩控差最大为 m*20 dp[0][Min] = 0; ///起始状态要先置为 0 /// k 的最大取值范围是[-Min, Min], 但是数组不能表示负数, 因此将数组向右平移 Min,得到[0, 2*Min] for(i=0; i<=m; i++) { for(k=0; k<=Min*2; k++) { if(dp[i][k]==-1) continue; ///如果存在,接着找 dp[i][k] 的下一个状态 for(j=1; j<=n; j++) { if(dp[i+1][k+D[j]-P[j]] < dp[i][k]+D[j]+P[j]) { t1=i, t2=k; while(t1>0 && pre[t1][t2]!=j) { t2 -= D[pre[t1][t2]] - P[pre[t1][t2]]; t1 --; } if(t1==0) ///当 t1 为 0 时,编号为 j 这个人在之前没有被选中过 { dp[i+1][k+D[j]-P[j]] = dp[i][k] + D[j] + P[j]; pre[i+1][k+D[j]-P[j]] = j; } } } } } int ff = Min; int num = 0, sum1=0, sum2=0; ///要选辩控差最小的,所求的 num 便是选 m 个人辩控差最小的 while(dp[m][ff-num]==-1 && dp[m][ff+num]==-1) num++; if(dp[m][ff-num]>dp[m][ff+num]) t2 = ff-num; else t2 = ff+num; t1 = m; for(i=1; i<=m; i++) { Answer[i] = pre[t1][t2]; sum1 += D[Answer[i]]; sum2 += P[Answer[i]]; t1--; t2 -= D[Answer[i]] - P[Answer[i]]; } printf("Jury #%d ", iCase++); printf("Best jury has value %d for prosecution and value %d for defence: ", sum1, sum2); sort(Answer+1, Answer+1+m); for(i=1; i<=m; i++) printf(" %d", Answer[i]); printf(" "); } return 0; }
c题
https://vjudge.net/contest/355951#problem/C
总算有道水题,求子序列,前两天刚做过
大体优化的思路就是用upper踢掉原来的数
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; struct node { int l, w; }a[5005]; int dp[5005]; bool cmp(node a, node b) { if (a.w == b.w) return a.l < b.l; else return a.w < b.w; } int main() { int n, t; scanf("%d", &t); while (t--) { cin >> n; int i; for (i = 0; i < n; i++) scanf("%d%d", &a[i].l, &a[i].w); MEM(dp, INF); sort(a, a + n, cmp); for (i = n - 1; i >= 0; i--) dp[ lower_bound(dp, dp + n, a[i].l)-dp] = a[i].l; printf("%d ", lower_bound(dp, dp + n, INF) - dp); } }
d题
https://vjudge.net/contest/355951#problem/D
还是子序列,事实告诉我们不能被英语打败
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; int n; int a[500005]; int s[500005]; int main() { int t; scanf("%d", &t); while(t--) { scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &a[i]); s[0]=0; int ans=0; for(int i=1; i<=n; i++) { if(a[i]>s[ans]) { s[++ans]=a[i]; continue; } int q=upper_bound(s, s+ans, a[i])-s; s[q]=a[i]; } printf("%d ", ans); } return 0; }
e题
https://vjudge.net/contest/355951#problem/E
2个小时的找bug时间竟然是因为一个大括号。。。
这题的思路是开个二维的dp数组
一边记录从左侧下的最优方案,另一个记录右侧的
然后从底层往上慢慢推就行
重要的是经验:对于这种只有两种选择的题可以尝试分别记录
然后注意细节!!!
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; struct node { int x; int y; int high; bool operator < (const node &other)const{ return high < other.high; } }; node p[1005]; int dp[1005][2]; int x, y, n, m; int qw; void left(int i) { int k = i - 1; while (k > 0 && p[i].high - p[k].high <= m) { if (p[i].x >= p[k].x && p[i].x <= p[k].y) { dp[i][0] = p[i].high - p[k].high + min(p[i].x - p[k].x + dp[k][0], p[k].y - p[i].x + dp[k][1]); return; } else k--; } if (p[i].high - p[k].high > m) dp[i][0] = INF; else dp[i][0] = p[i].high; } void right(int i) { int k = i - 1; while (k > 0 && p[i].high - p[k].high <= m) { if (p[i].y >= p[k].x && p[i].y <= p[k].y) { dp[i][1] = p[i].high - p[k].high + min(p[i].y - p[k].x + dp[k][0], p[k].y - p[i].y + dp[k][1]); return; } else k--; } if (p[i].high - p[k].high > m) dp[i][1] = INF; else dp[i][1] = p[i].high; } int main() { int t; cin >>t; while (t--) { cin >> n >> x >> y >> m; for (int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y >> p[i].high; p[0].high = 0; p[0].x = -20000; p[0].y = 20000; p[n + 1].high = y; p[n + 1].x = p[n + 1].y = x; sort(p, p + n + 2); for (int i = 1; i <= n + 1; i++) { left(i); right(i); } int ans = min(dp[n + 1][0], dp[n + 1][1]); printf("%d ", ans); } }
f题
https://vjudge.net/contest/355951#problem/F
一眼01背包
但是好像又不太一样
这道题存在负数还是两个参数怎么办
负数------坐标横移
两个参数-----把其中的一个作为dp数组的下标
一发过了是我没想到的。。这不是我的风格
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; int s[1005]; int f[1005]; int dp[200050]; int main() { int n; while (cin >> n) { MEM(dp, -INF); for (int i = 0; i < n; i++) { cin >> s[i] >> f[i]; } dp[100000] = 0; for (int i = 0; i < n; i++) { if (s[i] < 0 && f[i] < 0) continue; if (s[i] > 0) { for (int j = 200000; j > s[i]; j--) { if (dp[j - s[i]] > -INF) dp[j] = max(dp[j], dp[j - s[i]] + f[i]); } } else { for (int j = s[i]; j < 200000 + s[i]; j++) if (dp[j - s[i]] > -INF) dp[j] = max(dp[j], dp[j - s[i]] + f[i]); } } int ans = -INF; for (int i = 100000; i <= 200000; i++) { if (dp[i] >= 0) ans = max(ans, dp[i] + i - 100000); } printf("%d ", ans); } }
G题
https://vjudge.net/contest/355951#problem/G
挺简单的题但是还是卡了半天
问题在于我一开始想把dp[i]定义为用前i个牛能达到的最高高度,但是没写出来
然后我定义dp[i]类似为一个bool数组记录该高度是否能达到
希望有大佬看到这里能私聊我讨论讨论第一种思路的可行性
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; struct node { int h, a, c; }p[405]; bool cmp(node x, node y) { return x.a < y.a; } int main() { int n; cin >> n; for (int i = 0; i < n; i++) { cin >> p[i].h >> p[i].a >> p[i].c; } sort(p, p + n, cmp); int dp[1000000]; int sum[100000]; MEM(dp, 0); dp[0] = 1; int ans = 0; for (int i = 0; i < n; i++) { MEM(sum, 0); for (int j = p[i].h; j <= p[i].a; j++) { if (!dp[j] && dp[j - p[i].h] && sum[j-p[i].h] < p[i].c) { sum[j] = sum[j - p[i].h] + 1; dp[j] = 1; ans = max(j, ans); } } } cout << ans; }
H题
https://vjudge.net/contest/355951#problem/H
数学题!
我数学好差。。公式好难推
推出来也超时。。
欸
不过这题除了推公式没啥别的了。
不会写数学符号直接上代码了
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <stdlib.h> #include <vector> #include <queue> #include <cmath> #include <stack> #include <map> #include <set> using namespace std; #define ms(x, n) memset(x,n,sizeof(x)); typedef long long LL; const int inf = 1<<30; const LL maxn = 1e5+10; const int mod = 1e6; int N, M, l, r, a[1010]; int dp[1010][maxn]; int main() { int input; cin >> N >> M >> l >> r; for(int i = 1; i <= M; ++i){ cin >> input; ++a[input]; } for(int i = 0; i <= N; ++i) dp[i][0] = 1; for(int i = 1; i <= N; ++i){ for(int j = 1; j <= M; ++j){ if(j-a[i]-1 >= 0) dp[i][j] = (dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1-a[i]]+mod)%mod; else dp[i][j] = (dp[i][j-1]+dp[i-1][j]+mod)%mod; } } int ans = 0; for(int i = l; i <= r; ++i) ans = (ans+dp[N][i]+mod)%mod; cout << ans << endl; return 0; }
I题
https://vjudge.net/contest/355951#problem/I
水题没啥说的
啥?你问我为啥水题还re?
----N,M不分
----啊,那没事了
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #include <sstream> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #define Mod 1000000007 #define eps 1e-6 #define ll long long #define INF 0x3f3f3f3f #define MEM(x,y) memset(x,y,sizeof(x)) #define Maxn 100005 #define P pair<int,int> using namespace std; struct node { int s; int e; int v; }q[1005]; bool cmp(node x, node y) { return x.e < y.e; } int main() { int n, m, r; int d; while(cin >> n >> m >> r) { for (int i = 0; i < m; i++) { cin >> q[i].s >> q[i].e >> q[i].v; } ll dp[10005]; sort(q, q + m, cmp); for (int j = 0; j <m; j++) { dp[j] = q[j].v; for (int i = 0; i < j; i++) { if (q[j].s >= q[i].e + r) { dp[j] = max(dp[j], dp[i] + q[j].v); } } } ll max = 0; for (int i = 0; i < m; i++) { if (dp[i] > max) max = dp[i]; } cout << max<<endl; } }
J题
https://vjudge.net/contest/355951#problem/J
自己写的代码三重循环tle了
看一下数据不tle才怪。。
学下网上大佬的思路吧
f[i][j]表示到第i段路满足不下降(只做不下降,不上升待会再做一遍),且当前高度为j所需要的最小花费。它的转移实际上是mink∈[1,j]f[i−1][k]+|h[i]−ori[j]|mink∈[1,j]f[i−1][k]+|h[i]−ori[j]|,ori[j]是j对应的离散化之前的数,h[i]是i原来的高度,因为不下降,所以只能从[1,j][1,j]转移。不过这样的复杂度是O(n3)O(n3)的,我们发现,随着jj的增大,kk的取值范围只是上界变大了,因此可以通过更新前缀和来维护minn[i]=minj=[1,i]{f[j]}minn[i]=minj=[1,i]{f[j]}。
真高级。。
来人,上代码!
#include<cstdio> #include<cstring> #include<algorithm> long long Abs(long long x) { return x>0?x:(-x); } long long Min(long long x,long long y) { return x<y?x:y; } struct node { long long a,b,i; friend bool operator <(node x,node y) { return x.i<y.i; } }a[2001]; bool cmp(node x,node y) { return x.a<y.a; } long long ori[2001]; long long f[2001];//f[i]代表f[i]到f[n]的最大值 int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i].a); a[i].i=i; } std::sort(a+1,a+n+1,cmp); int cnt=0; a[0].a=-1; for(int i=1;i<=n;++i) { if(a[i].a!=a[i-1].a) { ++cnt; ori[cnt]=a[i].a; } a[i].b=cnt; } std::sort(a+1,a+1+n); long long ans=1000000000000000ll; memset(f,0,sizeof(f)); f[cnt+1]=10000000000000ll; for(int i=n;i;--i) { for(int j=1;j<=cnt;++j) f[j]+=Abs(a[i].a-ori[j]); for(int j=cnt;j;--j) f[j]=Min(f[j],f[j+1]); } for(int i=1;i<=cnt;++i) ans=ans<f[i]?ans:f[i]; printf("%lld ",ans); return 0; }