B.好伙计猜拳
题目链接👈
题目描述🥰
题目思路😀
这个题目初看就是一个很典型的动态规划问题,但是状态的表示在我看来也是比较巧妙的,所以在比赛的过程中也是没有用动态规划解出这道题😇
动态规划主要是状态表示和状态的转移方程
状态表示:
- 用dp[i][0]表示在第i个记录没有进行交换的情况下使前i个记录合法的最小代价
- 用dp[i][1]表示在第i个记录进行了交换的情况下使前i个记录合法的最小代价
状态转移方程:(1<=i<=n,1<=j<i )
- a[j]<=a[i] && b[j]<=b[i](两个已经符合递增的规则)
- 所以当第j个记录没有交换的情况下,第i条记录也不用进行交换,只需要删除两个记录之间的记录。dp[i][0]=min(dp[i][0],dp[j][0]+c1*(i-j-1))
- 当第j个记录已经交换的情况下,第i条记录也需要进行交换,与此同时,也需要删除两个记录之间的记录。dp[i][1]=min(dp[i][1],dp[j][1]+c2+c1*(i-j-1))
-
a[i]>=b[j] && b[i]>=a[j](两个在任意一个交换后即可符合递增的规则)
- 在第j个记录交换的情况下,第i条记录就不用进行交换,所以也只需要删除两个记录之间的记录。dp[i][0]=min(dp[i][0],dp[j][1]+c1*(i-j-1));
- 当第j个记录没有交换的情况下,第i条记录需要进行交换,与此同时,也需要删除两个记录之间的记录。dp[i][1]=min(dp[i][1],dp[j][0]+c2+c1*(i-j-1));
最重要的是需要对于边界的处理:为了保证最后的序列的整体合法,所以我们可以加一个一定在最后面的记录。
AC代码🧠
// Problem: 好伙计猜拳
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95338/B
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define dev1(a) cout << #a << '=' << a << endl;
#define dev2(a, b) cout << #a << " = " << a << " " << #b << " = " << b << endl;
#define dev3(a, b, c) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << endl;
#define dev4(a, b, c, d) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << " " << #d << " = " << d << endl;
#define dev5(a, b, c, d, e) cout << #a << " = " << a << " " << #b << " = " << b << " " << #c << " = " << c << " " << #d << " = " << d << " " << #e << " = " << e << endl;
#define vec(a) \
for (int i = 0; i < a.size(); i++) \
cout << a[i] << ' '; \
cout << endl;
#define darr(a, _i, _n) \
cout << #a << ':'; \
for (int ij = _i; ij <= _n; ij++) \
cout << a[ij] << ' '; \
cout << endl;
#define cin(a,n) \
for(int i=0;i<n;i++) \
cin>>a[i];
#define endl "\n"
#define pow pim
int pim(int a,int k)
{
int res=1;
if(a==0)return 0;
while(k)
{
if(k&1)res=(int)res*a;
k>>=1;
a=(int)a*a;
}
return res;
}
#define fi first
#define se second
#define caseT \
int T; \
cin >> T; \
while (T--)
#define int long long
// #define int __int128
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MOD = 99999999;
// const int N = ;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void solve()
{
int n,c1,c2;
cin>>n>>c1>>c2;
n++;
vector<int>a(n+1),b(n+1);
for(int i=1;i<n;i++)cin>>a[i]>>b[i];
//要设置一个来保证最终的序列合法
a[n]=b[n]=1e9;
vector<vector<int>>dp(n + 1, vector<int> (2));
// dp[i][0]表示前i条记录在第i条记录没有交换的条件下需要的最小代价
//定义边界
for(int i=1;i<=n;i++)
for(int j=0;j<2;j++)
dp[i][j]=1e18;
for(int i=1;i<=n;i++)
for(int j=i-1;j>=0;j--)
{
if(a[j]<=a[i]&&b[j]<=b[i])
{
dp[i][0]=min(dp[i][0],dp[j][0]+c1*(i-j-1));
//如果说第j条的记录已经交换过,为了维持这个序列的合法性,那么i记录也要进行交换,同时删除两个之间的记录
dp[i][1]=min(dp[i][1],dp[j][1]+c2+c1*(i-j-1));
}
if(a[i]>=b[j]&&b[i]>=a[j])
{
//因为现在的情况已经反了,所以在j已经交换了的情况下i不用交换,代价就是删除两个记录中间的记录的代价
dp[i][0]=min(dp[i][0],dp[j][1]+c1*(i-j-1));
//因为现在的情况已经反了,而且j还没进行交换,所以i就需要进行交换,代价就是交换第i条记录并删除两个之间的记录
dp[i][1]=min(dp[i][1],dp[j][0]+c2+c1*(i-j-1));
}
}
cout<<min(dp[n][0],dp[n][1])<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
caseT
solve();
return 0;
}
/*
*/
标签:
算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探