前言:一直想练练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\n", 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\n", iCase++); printf("Best jury has value %d for prosecution and value %d for defence:\n", sum1, sum2); sort(Answer+1, Answer+1+m); for(i=1; i<=m; i++) printf(" %d", Answer[i]); printf("\n\n"); } return 0; }
原文地址:https://www.cnblogs.com/cyq123/p/12296640.html
时间: 2024-10-10 13:07:45