弱鸡只做出来五题。。
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6775
小沃沃在玩一个有趣的游戏。
初始他有 \(n\) 块钱,每一轮他需要投入至少 \(m\) 块钱,系统会拿走其中 \(p\%\) 的钱,并把剩下的钱还给他。
请问在最优情况下,小沃沃最多可以玩多少轮?
假设当前一轮小沃沃投入了 \(x\) 块钱,那么他可以收回$ ?x\times (100?p)/100 ? $块钱,其中 \(?a?\) 表示 \(a\) 取下整。
小沃沃每一轮投入的钱不能超过他现在拥有的钱。
每一轮投入的钱必须为整数。
那就是每一次就丢 \(m\) 块钱进去,除了最后一次之外,每一次丢的实际钱数其实是 \(m\) 减去返回的钱数。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m,p;
scanf("%d%d%d",&n,&m,&p);
if(n<m){
printf("0\n");
continue;
}
int x=ceil(m*p*1.0/100);
//返回的钱是向下取整,那丢出去的钱就是想上取整
printf("%d\n",(n-m)/x+1);
}
return 0;
}
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6776
给出一堆点到固定点的距离,问所有给出的点两两之间距离之和。
自然是把所有点放到一条线上和最短。
但是题目数据范围是 \(10^5\) 用 \(O(n^2)\)的方法是不行的。
我们可以用前缀和解决这个题目。把所有的距离按从小到大排好,用 \(arr\) 数组存放数据,用 \(sum\) 数组记录前缀和。
我们不需要重复记录两个点之间的距离,所有我们假定从最大的开始计算。
这样我们就可以用\(O(n)\)复杂度实现了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
long long arr[MAXN];
long long sum[MAXN];
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",arr+i);
sort(arr+1,arr+1+n);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+arr[i];
long long res=0;
for(int i=n;i;i--)res+=(i-1)*arr[i]-sum[i-1];
printf("%lld\n",res);
}
return 0;
}
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6777
有 \(n\) 个人,会在不同时刻出现在不同位置,其中 \(1\) 号人物是感染者,在同一时间同一地点一起出现的人会被感染者感染,问最后哪些人感染了。
我们可以用时间作为第一关键字,地点作为第二关键字排序,在循环一遍看看哪些人是在同一时间同一地点出现了,把这些人存放在一个\(vector\) 里面,如果出现了感染者就全部感染,否则就清空。
#include <bits/stdc++.h>
using namespace std;
const int MAXM=2e5+10;
const int MAXN=2e4+10;
int vis[MAXN];
struct node{
int t,p;
int id;
bool operator<(const node a)const {
return t==a.t?p<a.p:t<a.t;
//以时间为第一关键字,地点为第二关键字排序
}
}arr[MAXM];
int cnt;
int n;
vector<int>tem;
int main(){
int t;
scanf("%d",&t);
while(t--){
cnt=1;
scanf("%d",&n);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++){
int m;
scanf("%d",&m);
while(m--){
scanf("%d%d",&arr[cnt].t,&arr[cnt].p);
arr[cnt].id=i;
cnt++;
}
}
arr[0].t=0,arr[0].p=0,arr[0].id=1;
sort(arr,arr+cnt);
tem.clear();
int f=0;
vis[1]=1;
for(int i=1;i<cnt;i++){
if(arr[i].t==arr[i-1].t&&arr[i].p==arr[i-1].p)tem.push_back(arr[i].id);
//同一时间同一地点的人加到tem
else {
if(f){
//存在感染者则全部感染
for(auto x:tem)vis[x]=1;
f=0;
}
tem.clear();//清空
tem.push_back(arr[i].id);
}
if(vis[arr[i].id])f=1;
}
if(f)for(auto x:tem)vis[x]=1;
tem.clear();
for(int i=1;i<=n;i++)
if(vis[i])tem.push_back(i);
//因为最后不能有空格,比赛的时候没咋考虑写法就写成这样了,其实就是输出
for(int i=0;i<tem.size();i++){
if(i==tem.size()-1)printf("%d",tem[i]);
else printf("%d ",tem[i]);
}
printf("\n");
}
return 0;
}
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6778
给一堆车牌,可以对最后一位进行限制通行,每个尾号最多限制一次,问五天内哪种限制方法的单日最高可通行车辆数最少。
发现尾号最多就 \(0-9\) 就这么十个数字,那对于每天限制哪些我们可以通过全排列枚举。
但是另一个限制就是,怎么分配每天限制的尾号数量,毕竟不能一天把所有尾号都限行或者平均分。
每天分配的限制其实就只有7种情况。
周一 | 周二 | 周三 | 周四 | 周五 |
---|---|---|---|---|
1 | 1 | 1 | 1 | 6 |
1 | 1 | 1 | 2 | 5 |
1 | 1 | 1 | 3 | 4 |
1 | 1 | 2 | 2 | 4 |
1 | 1 | 2 | 3 | 3 |
1 | 2 | 2 | 2 | 3 |
2 | 2 | 2 | 2 | 2 |
那么我们在全排列的时候分别求这其中情况下的最值就行了。
注意,对于周一限制尾号\(1\),周二限制尾号\(2\),周三限制尾号\(3\),周四限制尾号\(4\),周五限制尾号\(5,6,7,8,9,0\),和周一限制尾号\(5,6,7,8,9,0\),周二限制尾号\(2\),周三限制尾号\(3\),周四限制尾号\(4\),周五限制尾号\(1\),应当看成一种情况,所以只存在上面的7种情况。最后900ms压线过。
感觉里面套个循环有点不好写就直接。。。
#include <bits/stdc++.h>
using namespace std;
int arr[10]={0,1,2,3,4,5,6,7,8,9};
int sum[15];
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(sum,0,sizeof sum);
char str[10];
for(int i=0;i<n;i++)scanf("%s",str),sum[str[4]-‘0‘]++;
int res=1e9;
do{
int ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]);
ans=max(ans,n-sum[arr[5]]-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]);
ans=max(ans,n-sum[arr[1]]-sum[arr[2]]);
ans=max(ans,n-sum[arr[3]]-sum[arr[4]]);
ans=max(ans,n-sum[arr[5]]-sum[arr[6]]);
ans=max(ans,n-sum[arr[7]]-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
ans=0;
ans=max(ans,n-sum[arr[0]]-sum[arr[1]]);
ans=max(ans,n-sum[arr[2]]-sum[arr[3]]);
ans=max(ans,n-sum[arr[4]]-sum[arr[5]]);
ans=max(ans,n-sum[arr[6]]-sum[arr[7]]);
ans=max(ans,n-sum[arr[8]]-sum[arr[9]]);
res=min(ans,res);
}while(next_permutation(arr,arr+10));
printf("%d\n",res);
}
return 0;
}
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6781
-.- 太长就不赘述了。
一开始以为是贪心,能比bob快就做,不能就跳。结果连样例都过不了。。
这种题目贪心不行自然就想DP了。
定义 \(DP[i][j]\) ,表示在前 \(i\) 题中做出来 \(j\) 题用的时间。初始化为\(10^{18}\)
因为bob是顺序做题的,而题目给出的条件可以保证bob每一题都做,且每一题的时间都花完,那么我们可以用 \(sum[i]\) 表示bob做完前 \(i\) 题用的时间。用 \(arr[i]\) 表示alice单独做第 \(i\) 题的时间。
那么转移方程就是
如果尝试的题目大于做出来的题目时,我们可以从前一题同样做出来 \(j\) 题的情况转移过来。
如果当前题目能做出来,则判断做这道题能否更快。
最后看在尝试 \(n\) 题的情况下,\(dp[n][i]!=10^{18}\) 当做 \(i\) 的最大值就是答案。
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e3+10;
long long arr[MAXN],brr[MAXN],sum[MAXN];
long long dp[MAXN][MAXN];
const long long INF=1e18;
int n;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",arr+i);
for(int i=1;i<=n;i++)scanf("%lld",brr+i),sum[i]=sum[i-1]+brr[i];
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)dp[i][j]=INF;
for(int i=0;i<=n;i++)dp[i][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
if(i>j)dp[i][j]=min(dp[i][j],dp[i-1][j]);
if(arr[i]+dp[i-1][j-1]<=sum[i])dp[i][j]=min(dp[i][j],arr[i]+dp[i-1][j-1]);
}
}
for(int i=n;i>=0;i--){
if(dp[n][i]!=INF){
printf("%d\n",i);
break;
}
}
}
return 0;
}
原文:https://www.cnblogs.com/jianjinxiani/p/13380855.html