HDU2059
theme:龟兔赛跑,跑道长度为L,兔子速度为VR,乌龟骑一辆电动车,电动车速度为V1,充满电情况下,电动车能以V1速度骑行C米,没电时乌龟推车速度为V2,开始时为充满电状态,途中有n个充电站a[i](不在起终点),每次充电需要等t秒,乌龟可以选择充还是不充。问乌龟有没有可能赢兔子。
solution:将起点与终点也看做充电站,令dp[i]表示从起点出发到第i个充电站所需的最短时间,我们考虑走到a[i]的途中最后一个充电的地方是哪,则可推得dp[i]=min(dp[i],dp[j]+t),其中j为i之前的充电站,t为从j充满电后不再充电走到i的时间。注意特殊处理一下j=0时,因为起点时已充满电,不用加上充电的时间。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define far(i,n) for(int i=0;i<n;++i)
#define fdr(i,n) for(int i=n-1;i>=0;--i)
typedef long long ll;
int a[105];
double dp[105];
int main()
{
int L;
while(~scanf("%d",&L))
{
int n,c,t;
scanf("%d%d%d",&n,&c,&t);
int vr,v1,v2;
scanf("%d%d%d",&vr,&v1,&v2);
a[0]=0;
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
a[n+1]=L;
far(i,n+2)
dp[i]=2147483647;
dp[0]=0;
for(int i=1;i<=n+1;++i)
for(int j=0;j<i;++j)
{
int l=a[i]-a[j];
int flag=0;
double tx;
if(c>=l)
tx=1.0*l/v1;
else
tx=1.0*c/v1+1.0*(l-c)/v2;
if(j==0)
flag=-t;
dp[i]=min(dp[i],dp[j]+tx+t+flag);
}
// far(i,n+2)
// cout<<i<<" "<<dp[i]<<endl;
double tr=1.0*L/vr;
if(dp[n+1]<tr)
printf("What a pity rabbit!\n");
else
printf("Good job,rabbit!\n");
}
}
-
0-1背包
1、饭卡 hdu2546
http://acm.hdu.edu.cn/showproblem.php?pid=2546
//√31ms 01背包的应用,要变形也变的是输出结果,不是背包模板!
/*
theme:有一张饭卡,给定原始余额为m,食堂规定只要余额>=5就可以购买任意价格的菜,及时减掉之后余额为负
,否则无法购买,及时钱够。现给定n种菜的价格,问可以使卡的余额达到最低多少?(多组案例)
input:多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。
output:
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
*
*
*
*
*
*
solution:因为01背包算的是余额为i的背包能装下的最大物品(价值),所以我们可以
做减法,算出余额为i的卡最多能消费多少钱,再用开始余额减就可以得到最小余额
有一点要注意就是有个5元的限制条件,所以先利用贪心思想,从m元中拿出5元来买最贵的菜,
再用01背包算m-5的最大消费;一个菜
当然,如果m一开始就小于5则直接输出m(的d[m-5]会错!)
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<cstring>
#include<queue>
using namespace std;
const int Max=1010;
int price[2000];
int dp[2000];
void zeroOneKnapsack(int maxv,int costs,int gains)
{
for(int i=maxv;i>=costs;--i)
dp[i]=max(dp[i],dp[i-costs]+gains);
}
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;++i)
scanf("%d",&price[i]);
sort(price,price+n);
int m;
scanf("%d",&m);
if(m<5){
printf("%d\n",m);
continue;
}
for(int i=0;i<n-1;++i)
zeroOneKnapsack(m,price[i],price[i]);
printf("%d\n",m-dp[m-5]-price[n-1]);
}
}
/*
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
1
4
3
0
-45
32
3
*/
此题还可计算一下所有菜价格之和,若<m则输出m-accumulate(price,price+n,0)
- 最长非降子序列
http://acm.hdu.edu.cn/showproblem.php?pid=1257
//√31ms(1000)
/*
theme:有n个导弹依次发射过来,给出它们的发射高度,问至少要几个拦截系统可以拦截这n个导弹。
其中每个拦截系统发出的第一颗炮弹可以是任意高度,之后的每颗导弹高度不能超过上一颗。
*
*
*
*
*
solution:其实就是求最长非降子序列!令cnt[i]表示以missile[i]结尾的前i个数的最长非降子序列,
则cnt[i]等于1、每个满足missile[j]<missile[i](因为此题是不能超过,所以不能取=)的cnt[j]+1的max值。
最终的结果就是求cnt[]数组中最大元素。
*/
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int missile[300005];
int n;
int cnt[300005];
int dp()
{
int ans=0;
for(int i=0;i<n;++i)
{
cnt[i]=1;
for(int j=0;j<i;++j)
if(missile[j]<missile[i])
cnt[i]=max(cnt[i],cnt[j]+1);
ans=max(ans,cnt[i]);
}
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;++i)
scanf("%d",&missile[i]);
int ans=dp();
printf("%d\n",ans);
}
}
/*
4 1 2 1 2
8 389 207 155 300 299 170 158 65
5 1 2 3 5 4
5 1 3 5 2 4
2
2
4
3
*/
- 最大连续子段和
hdu1231:http://acm.hdu.edu.cn/showproblem.php?pid=1231
//OK 171ms(1000)
//注意结构体的运算符重载
//定义ans[i].sum为以a[i]结尾的最大连续子序列,所以若ans[i-1]>0,则ans[i].sum=ans[i-1].sum+a[i]
//否则ans[i].sum=a[i];最后求最大的ans[i]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define far(i,n) for(int i=0;i<n;++i)
typedef long long ll;
ll a[10020];
int n;
struct _dp
{
ll start;
ll last;//起始和结束下标
int flag;//标记start改变了没
ll sum;
bool operator<(const _dp &b)
{
if(sum==b.sum)
if(start==b.start)
return last>b.last;
else
return start>b.start;
return sum<b.sum;
}
}ans[10020];
void dp(int n)
{
ans[0].sum=a[0];
for(int i=1;i<n;++i)
{
if(ans[i-1].sum>0)
{
ans[i].sum=ans[i-1].sum+a[i];
ans[i].start=ans[i-1].start;
ans[i].last=i;
}
else
{
ans[i].sum=a[i];
ans[i].start=i;
ans[i].last=i;
}
}
/*far(i,n)
cout<<ans[i].sum<<" "<<ans[i].start<<" "<<ans[i].last<<endl;*/
_dp output=*max_element(ans,ans+n);
if(output.sum<0)
printf("0 %lld %lld\n",a[0],a[n-1]);
else
printf("%lld %lld %lld\n",output.sum,a[output.start],a[output.last]);
}
int main()
{
while(scanf("%d",&n)&&n)
{
far(i,n)
scanf("%lld",&a[i]);
dp(n);
}
}
/*
6
-2 11 -4 13 -5 -2
10
-10 1 2 3 4 -5 -23 3 7 -21
6
5 -8 3 2 5 0
1
10
3
-1 -5 -2
3
-1 0 -2
0
20 11 13
10 1 4
10 3 5
10 10 10
0 -1 -2
0 0 0
*/
-
最大矩阵和
hdu1506http://acm.hdu.edu.cn/showproblem.php?pid=1506
//ok 109ms(1000)
/*theme:即求最大矩阵和
*
*
*
solution:考虑ans[i]为以a[i]的高为高所能形成的最大的矩阵,所以最终结果为max(ans)
即求每个a[i]左右两边分别能到达的不小于它的最大下标。
用动态规划思想(避免重复计算)减少耗时:考虑若左边比它大,则a[i-1]左边能到达的a[i]
一定能到达。右边同理,但注意:用DP求右边时需从n-1往左求
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define far(i,n) for(int i=0;i<n;++i)
#define fdr(i,n) for(int i=n-1;i>=0;--i)
typedef long long ll;
ll a[100020];
int L[100020];
int R[100020];
ll ans[100020];
ll dp(int n)
{
L[0]=0;
R[n-1]=n-1;
far(i,n)
{
L[i]=i;
int cnt=i;
while(cnt>0)//cnt为当前已比的位置
{
if(a[cnt-1]>=a[i])
L[i]=L[cnt-1];
else
break;
cnt=L[cnt-1];
}
}
fdr(i,n)
{
R[i]=i;
int cnt=i;
while(cnt<n-1)
{
if(a[cnt+1]>=a[i])
R[i]=R[cnt+1];
else
break;
cnt=R[cnt+1];
}
}
far(i,n)
ans[i]=a[i]*(R[i]-L[i]+1);
/*far(i,n)
cout<<ans[i]<<" ";
cout<<endl;*/
return *max_element(ans,ans+n);
}
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
far(i,n)
scanf("%lld",&a[i]);
printf("%lld\n",dp(n));
}
}
/*
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
8
4000
*/