您的位置:首页 > 其它

ACdream DP专题训练

2015-07-26 17:15 363 查看


A - 小彭玉的扫荡食堂计划

Time Limit: 20000/10000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)


Problem Description

哗啦啦村的食堂很奇怪,就是如果这个饭卡所剩金额低于5元的话,这个饭卡就不能刷了。
也就是说,只要这个饭卡金额大于等于5元,就可以随便刷~

有一天,小彭玉看了看哗啦啦食堂的饭,“哇,好好吃!我要全部都买下来!”
但是小彭玉并没有那么多钱,于是他准备充分利用自己的钱,去买这些食物!
请问最后小彭玉的饭卡余额最少能到多少?


Input

多组测试数据(最多100组)
第一行 n,表示有n个菜
第二行 接下来n个数字,a[i]表示第i道菜多少钱
第三行 一个数m,表示小彭玉的饭卡,一开始有m元
1<=n<=1000,1<=a[i]<=10000,1<=m<=10000


Output

输出一个整数,表示最后饭卡显示的金额数


Sample Input

1
10000
6
10
1 2 3 2 1 1 2 3 2 1
50




Sample Output

-9994
32


解题思路:
由于饭卡小于5元就不能使用了,但是没规定5元以上不能刷超过的多少钱的东西,所以就算是负数也没关系(题目说随便刷)。所以我们可以枚举数组中的最大值,然后把输入当前总的钱数-5就是扣掉最大值之后能取到的最大值,很显然是个背包问题,套一下01背包的DP就能出来了。
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 10100;

int dp[maxn];
int a[maxn];

int main()
{
int m;
int total;
int res;
int i,j;
int Max;
while(cin>>m)
{
memset(dp,0,sizeof(dp));
for(i=0;i<m;i++)
{
cin>>a[i];
}
cin>>total;
Max = a[0];
for(i=1;i<m;i++)
{
if(a[i] > Max)
{
Max = a[i];
}
}
//cout<<Max<<endl;
bool flag = false;
if(total < 5)
{
cout<<total<<endl;
continue;
}
else
{
for(i=0;i<m;i++)
{
if(a[i] == Max && !flag)
{
flag = true;
continue;
}
else
{
for(j=total - 5;j>=a[i];j--) //只要大于5就可以再买那个最大的
{
if(dp[j] < dp[j-a[i]] + a[i])
{
dp[j] = dp[j-a[i]] + a[i];
}
}
}
}
}
cout<<total - Max - dp[total - 5]<<endl;
}
return 0;
}



E - 喵哈哈的日常选数问题

Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)


Problem Description

喵哈哈村子的TTT同学比较怪,他非常讨厌一类数字,是哪种呢?
就是讨厌那些含有37或者4的数
比如 21379,123485,12379。
但是他并不讨厌928357这个数,因为他即不包含37,也没有4。

现在你[L,R]的区间,问你在这个区间中,最多能够选出多少个TTT同学不讨厌的数呢?


Input

输入两个整数,表示L和R
1 <= L <= R <= 2000000000 。


Output

输出一个整数,表示选出的数的个数


Sample Input

1 10



Sample Output

9


解题思路:数位DP问题,数位DP这个链接讲得挺清楚的,ACdream的数据有点小弱,第一次高位和低位写反,竟然还能AC。
AC代码:
#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
int dp[10][10];
void init()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=10;i++)
{
for(int j=0;j<10;j++)//枚举第i位可能出现的数
{
for(int k=0;k<10;k++)//枚举第i-1位可能出现的数
{
if(j!=4&&!(j==3&&k==7))
dp[i][j]  += dp[i-1][k];
}
}
}
}
int solve(int n)
{
init();
int digit[10];
int len = 0;
while(n>0)
{
digit[++len] = n%10;
n/=10;
}
digit[len+1]=0;
int ans = 0;
for(int i=len;i;i--)
{
for(int j=0;j<digit[i];j++)
{
if(j!=4&&!(digit[i+1]==3&&j==7))
ans+=dp[i][j];
}
if(digit[i]==4||(digit[i]==7&&digit[i+1]==3)) //这里一开始写错,ACdream后台数据貌似小弱,还能AC
break;
}
return ans;
}
int main()
{
int l,r;
while(cin>>l>>r)
{
if(l+r==0)
break;
else
cout<<solve(r+1)-solve(l)<<endl;
}
return 0;

}



C - 哗啦啦村的扩建

Time Limit: 2000/1000MS (Java/Others) Memory Limit: 512000/256000KB (Java/Others)
Submit Status


Problem Description

呀呀呀,哗啦啦村在日渐发展中,越来越大了。
唐老师看到这欣欣向荣的情景,感到非常开心。
狗哥在旁边,“喏,我们村子扩建这么快,肯定用了不少钱吧?”
唐老师说:“是呀,不过这些钱都不及我零花钱的万万分之一。”

那么这时候问题来了,唐老师的零花钱至少应该有多少钱呢?
狗哥也想知道这道题的答案,于是他拜托了青君同学,了解到了村子扩建的费用。
啊,原来村子的扩建费用,就是修建道路的费用。
整个村子可以看作有n个房子,村子会修建n-1条道路,保证从任意房子可以到达任意其他房子。
那修建这n-1条道路的费用怎么记呢?对于每条道路,假设这条道路左边有x个房子,右边有y个房子,这条道路长度为k,那么费用就是k*|x-y|。

那么唐老师的零花钱至少有多少钱呢?现在你应该知道了吧。


Input

第一行一个整数,表示这个村子有n个房子
接下来n-1行,表示每条道路的信息,三个整数 a,b,c,表示a,b之间有一条道路,这条路的长度为c
1<=n<=50,000
1≤ai, bi≤n
0 ≤ci≤ 10^6


Output

输出一个整数,表示唐老师的零花钱至少有多少钱


Sample Input

6
1 2 1
1 3 1
1 4 2
6 3 1
5 2 1



Sample Output

2000000000


解题思路:前向星+深搜就可以过,叶节点的累加值初始化为1,深搜更新每个结点的累加值,然后通过前向星枚举每条边得解,注意,输出的时候8个0直接输出,不要再乘,还有注意权值要开long long,统计的结果也要开long long.

AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

const int maxn = 1000010;
bool vis[maxn];
int head[maxn];
int cnt;
int count[maxn];
int m;
long long sum;

struct Edge
{
int to;
int next;
long long w;
}edge[maxn];

void add(int u,int v,int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}

void dfs(int pos)
{
int i;
vis[pos] = true;
for(i=head[pos];i;i=edge[i].next)
{
if(vis[edge[i].to] == true) continue; //边的邻接点
else
{
dfs(edge[i].to);
count[pos] += count[edge[i].to];
edge[i].w = edge[i].w * abs(m - 2 * count[edge[i].to]);
sum += edge[i].w;
}
}
}

int main()
{
int i;
int num1,num2,length;
//freopen("1.txt","r",stdin);
while(cin>>m)
{
cnt = 1;
sum = 0;
for(i=1;i<=m;i++)
{
count[i] = 1; //叶子节点
head[i] = 0;
vis[i] = false;
}
for(i=0;i<m-1;i++)
{
scanf("%d%d%d",&num1,&num2,&length);
add(num1,num2,length); //无向图
add(num2,num1,length);
}
dfs(1);
if(sum != 0)
printf("%lld00000000\n",sum);
else
printf("0\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: