卡题卡得好难受orz。发现自己还是连最基本的dp都没有掌握,泪目QAQ 花了一天时间阅遍资料,还是多少有点启发的。剩下的就得靠diffuse mode了。
下面开始愉快的刷题模式。一共四十多道,慢慢填坑。
ROBBER:http://acm.hdu.edu.cn/showproblem.php?pid=2955
这道题一看就是0/1背包啦,因为每种只有拿或不拿两种选择。
但是和其他的又有所不同。首先一看这道题,第一反应就是用总概率P当容量。刚开始智障了,以为p相加得到不被抓的概率,后来想想应该是相乘啊喂!这还不算,如果用p当容量,p小数点后面有好多位,就算乘个比较大的数把它化整,也是行不通的。因为概率是相乘的。
所以得用物品总价值当背包容量。dp数组里的值是不被抓的概率。正确的状态转移方程应该为:dp[j]=max(dp[j],dp[j-q[i].money]*q[i].v)
初始化dp[0] = 1.
还被坑到了一个地方。。dp数组要开到10000,因为100*100.刚开始开到100提示超时,不知道为什么。
代码如下:
#include
#include
#include
#include
using namespace std;
typedef struct{
int money;
double p;
}Node;
Node r[10003];
int T;
double P;
int N;
int Mj;
double dp[10003];
int main(){
cin>>T;
while(T--){
cin>>P>>N;
int sum = 0;
for(int i = 1;i<=N;i++){
cin>>r[i].money>>r[i].p;
sum += r[i].money;
}
dp[0] = 1;//不被抓概率
for(int i = 1;i<=N;i++)
for(int j = sum;j>=r[i].money;j--)
dp[j] = max(dp[j-r[i].money]*(1-r[i].p),dp[j]);
for(int i = sum;i>=0;i--){
if((1-dp[i])<=P){
printf("%d\n",i);
break;
}
}
memset(dp,0,sizeof(dp));
}
return 0;
}
最大报销额 http://acm.hdu.edu.cn/showproblem.php?pid=1864
这道题需要注意:
1.输入格式问题。scanf中%*+数据类型,意为输入的时候跳过此数据类型。还有规定格式输入。
2.因为都是两位double,所以要×100转换为int,才可转换为背包问题。
局部变量一定要初始化初始化初始化!!!debug了半天orz
做到这题的时候又意识到自己对dp之认识不足,又去翻翻翻资料。开始用了很奇怪的看上去是dp实则为贪心的算法,用票数当背包容量什么鬼啊摔!
代码如下:
#include
#include
#include
#include
using namespace std;
typedef struct{
int a;
int b;
int c;
int total;
}Node;
Node shu[33];
int dp[3000003];
int main(){
double Q;
int N;
cin>>Q>>N;
while(N!=0){
// cout<<"2333"<60000||A.b>60000||A.c>60000)
flag = false;
A.total = A.a+A.b+A.c;
if(A.total>100000)
flag = false;
if(flag){
shu[++index] = A;
// cout<=shu[i].total;j--)
dp[j] = max(dp[j-shu[i].total]+shu[i].total,dp[j]);
printf("%.2lf\n",(double)dp[(int)Q]/100.00);
memset(dp,0,sizeof(dp));
memset(shu,0,sizeof(shu));
cin>>Q>>N;
}
return 0;
}
哈哈哈看了这几天dp还是有用的嘛!竟然1A简直不能再开心啊哈哈哈哈
这道题关键的状态转移方程。dp[i]表示以num[i]为最后一项的连续子序列的最大值。这样就好啦!
分两种情况,一种是dp[i-1]<=0的。小于0说明越加越小,肯定不行。试了试这个地方加不加等号都可以A,题目中说的i,j必须最小,感觉不是很清楚。如果按题目所说,那么应该不加等号是正确的。
另一种,大于0的时候直接加加加就行。
同时每一步对应dp[i]记下此时子序列的长度。输出的时候逆推就好啦
代码如下
#include
#include
#include
#include
#include
using namespace std;
int N;
int num[10004];
int dp[10004];
int len[10004];
int main(){
scanf("%d",&N);
while(N!=0){
bool flag = false;
for(int i = 1;i<=N;i++){
scanf("%d",&num[i]);
if(num[i]>=0)
flag = true;
}
int ma = -99999999;
if(!flag){
cout<<0<<" "<>N;
continue;
}
else{
dp[0] = -99999999;
int sum = 0;
for(int i = 1;i<=N;i++){
if(dp[i-1]<=0){
dp[i] = num[i];
len[i] = 0;
}
else{
dp[i] = num[i] + dp[i-1];
len[i] = len[i-1]+1;
}
ma = max(dp[i],ma);
}
}
// printf("2333\n");
int end = 99;
for(int i = 1;i<=N;i++){
if(dp[i]==ma){
end = i;
break;
}
}
// cout<>N;
}
return 0;
}
max sum http://acm.hdu.edu.cn/showproblem.php?pid=1003
简直智障。。数组开小了,提示超时。调了半天orz
其实和上面一道题一毛一样
代码如下
#include
#include
#include
#include
#include
using namespace std;
int N;
int num[100004];
int dp[100004];
int len[100004];
int main(){
scanf("%d",&N);
int m = N;
int mar = 0;
while(N--){
int ma = -99999999;
dp[0] = -99999999;
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++)
scanf("%d",&num[i]);
for(int i = 1;i<=n;i++){
if(dp[i-1]<0){
dp[i] = num[i];
len[i] = 0;
}
else{
dp[i] = num[i] + dp[i-1];
len[i] = len[i-1]+1;
}
ma = max(dp[i],ma);
}
int end = 99;
for(int i = 1;i<=n;i++){
if(dp[i]==ma){
end = i;
break;
}
}
// while(num[end]==0&&len[end]!=0)
// end--;
mar++;
cout<<"Case "<