PS: 欠了好多期训练我都没有总结了。。。。。
好难受啊,一直准备贴个dp的训练集合出来。
hdu题号:
HDU 4565 —-4575
A
数学题: 精度问题很坑,小学弟的精度很高没搞过,然后调低精度a了。。。。。
B
全场都没人过。。。。 好像不会
C
一个比较难想的dp。。。
题意:给定所有人初识的排列,以及所有人将会挑战(和他前一位进行战斗)赢的次数。。。如果赢的话,他们会交换位置。
问:给的情况是否能够成立, 如果成立是否有多重结果?
比如:
3
0 1 1
可以有如下几种结果: 123
3和2挑战->132 , 2和3挑战->123
2和1挑战->213 , 3和1挑战->231
…
这个题的关键问题是:我们不知道先执行谁的挑战
但可以知道:如果某个人的ai>i-1 ,那么他必须要先向后移动
这就是解题的关键
我们设 dp[i] 表示i+1 超过前面i 个人,到达第一名需要的总次数。
则dp[i]=dp[i-1]+v[i]+1;
如果某个人的V 比超过前面所有人(dp[i])都还多,记录为k。我们只需要管最后k即可,因为最后一个可以满足前面所有的k。
那么我们接下来只需要知道 这个k最多能够向后移动多少位。 如果向后移动的次数+dp[i]
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) {
return b ? gcd(b, a % b) : a;
}
template<class T> T lcm(T a, T b) {
return a / gcd(a, b) * b;
}
using namespace std;
#define ll __int64
ll dp[1000005],v[1000005];
ll r;
int main()
{
//freopen("1.txt","r",stdin);
ll n,k,b;
int t=0;
while(~scanf("%I64d",&n))
{
t++;
if(t>=40) continue;
memset(dp,0,sizeof dp); //
for(int i=1;i<=n;i++)
{
scanf("%I64d",&v[i]);
dp[i]=dp[i-1]+v[i]+1;
if(dp[i-1]<=v[i])
k=i;
// printf("dp=%I64d\n",l[i]);
}
r=b=0;
for(int i=k+1;i<=n;i++)
{
if(v[i]<=b) b++; // 如果出现0 ,那么会减少贡献值(后面还有多少个v就减少多少个)。
r+=max((ll)0,v[i]-b); // 可以向后移动r ,0不会产生贡献值
}
if(r==v[k]-dp[k-1]) printf("%I64d\n",k);
else if(r<v[k]-dp[k-1]) printf("Bad Rescue\n");
else printf("Unknown\n");
}
return 0;
}
D
熊神说是最短路然后用状压dp做一做。抱大腿的感觉真好
E
数学题,嗨呀,抱大腿的感觉真好
F
dp,看见的比较晚,写了半个小时就过了。
题意: 给一串数字,可以分成几段:
ans=每一段首项a * 2^(这一段的长度) 求和
我们设 dp[i][j]:表示将前i个数字分成j段的最小值
有一个坑的地方时
长度=64,所以有些地方会爆longlong,所以要注意这些特殊情况的处理。
using namespace std;
#define ll __int64
/*
*/
ll dp[100][100]; //前i个分成j段的最小值
ll a[105];
ll pp[60];
void init(){ //
ll cnt=2;
for(int i=1;i<=62;i++){
pp[i]=cnt;
cnt*=2;
// printf("i=%d %I64d\n",i,pp[i]);
}
}
int main() {
//freopen("1.txt","r",stdin);
int w;
init();
scanf("%d",&w);
// int cas=1;
while(w--){
int n;
memset(dp,63,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]);
dp[1][1]=a[1]*2;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
for(int k=1;k<=j+1;k++){
if(k==1){
if(i<=62)
dp[i][1]=a[1]*pp[i];
continue;
}
if(i-j<=62)
dp[i][k]=min(dp[j][k-1]+a[j+1]*pp[i-j],dp[i][k]);
if(dp[i][k] > 1LL<<63-1 || dp[i][k]<0){
dp[i][k]= 1LL<<63-1;
}
// printf("%d %d %d =%I64d\n",i,j,k,dp[i][k]);
}
}
}
ll ans=1LL<<63 -1;
// printf("%I64d\n",ans);
for(int i=1;i<=n;i++){
ans=min(ans,dp[n][i]);
}
printf("%I64d\n",ans);
}
return 0;
}
G 简单dp
好气啊。
先跑最短路,然后跑一遍dp 就好了 ,好气啊。
H
没看,据说是个简单题。
J
其实可以做的,是个爆搜,最后还剩点时间,没敲出来,真菜。