http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId=339
为了做浙大月赛,洗澡都没有去成,哎要等到下个星期三才能洗成吧!比完赛立马写解题报告,可以涨涨访问量把!
A题:
思路:枚举全是2 和 5 为素因子组成的数形如 2^a*5^b,然后判断他们的max(a,b) 是否下于等于 2^a*5^b 用十进制表示的位数即可因为10=2*5
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int abs(int a)
{
return a<0?-a:a;
}
int solve(long long n)
{
long long ans=0,dd=1;
int bit=(int)(log10(n*1.0)+1.0000001);
// cout<<n<<" "<<bit<<endl;
for(int n1=0;dd<=n&&n1<=bit; dd*=2,n1++)
{
long long tmp=1;
int cnt=0;
for(int n2=0;tmp*dd<=n&&n2<=bit;n2++,tmp*=5) cnt+=(max(n1,n2)<=(int)(log10(tmp*1.0*dd)+1.0000001));
//cout<<dd<<" dd "<<cnt<<endl;
ans+=cnt;
}
return ans;
}
int main()
{
long long n,m;
while(cin>>m>>n)
{
cout<<solve(n)-solve(m-1)<<endl;
}
return 0;
}
J题:开始用dp做,发现不是超内存,就是超时间,然后一看才30,果断搜索,稍微剪一下枝就行了
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[30];
int N,M,ans;
void dfs(int id,int sum)
{
if(ans==M) return;
if(ans<sum) ans=sum;
for(int i=id;i<N&&a[i]+sum<=M;i++)
{
dfs(i+1,sum+a[i]);
}
}
int main()
{
long long tot;
while(scanf("%d%d",&N,&M)==2)
{
tot=0;
for(int i=0;i<N;i++)
scanf("%d",a+i),tot+=a[i];
if(tot<=M)
{
printf("%lld\n",tot);
continue;
}
sort(a,a+N);
ans=0;
dfs(0,0);
printf("%d\n",ans);
}
return 0;
}
K 题: 算是比较好的题目,分析题意后,仔细想想发现,如果第N天如果买西瓜的话,他只能从N-1天后后如果有西瓜那天开始截断,就是把之后多余的扔掉,开始买该天的习惯,那么果断贪心选择能维持到N-1 天后任意某一天后并且有西瓜的最小花费值,然后更新就行了,然后线段树就可以搞了,或者树状数组
#include <iostream>
#include <cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=500010;
typedef long long ll;
const ll inf=9999999999999LL;
ll d[maxn*4];
void build(int l,int r,int rt)
{
d[rt]=inf;
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int p,ll val,int l,int r,int rt)
{
if(l==r)
{
d[rt]=min(d[rt],val);
return;
}
int m=(l+r)>>1;
if(p<=m) update(p,val,lson);
else update(p,val,rson);
d[rt]=min(d[rt<<1],d[rt<<1|1]);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r) return d[rt];
int m=(l+r)>>1;
ll ret1=inf,ret2=inf;
if(L<=m) ret1=query(L,R,lson);
if(R>m) ret2=query(L,R,rson);
return min(ret1,ret2);
}
int day[maxn],cost[maxn];
int main()
{
int n;
while(scanf("%d",&n)==1)
{
for(int i=1;i<=n;i++)
scanf("%d",&cost[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&day[i]);
if(day[i]>n) day[i]=n;
}
build(1,n,1);
update(day[1],cost[1],1,n,1);
for(int i=2;i<=n;i++)
{
ll Min=query(i-1,n,1,n,1);
int pos=i-1+day[i];
if(pos>n) pos=n;
//cout<<Min<<endl;
update(pos,Min+cost[i],1,n,1);
}
printf("%lld\n",query(n,n,1,n,1));
}
return 0;
}
/*
4
10 20 1 40
3 2 3 1
*/
E题 发现时很简单的树形dp,但是为什么没人做呢,不懂得。dp[node ] [ m ] 表示回到node节点 遍历以node 为根的子树花费时间m的最优值,也就是获得最大财富。背包dp就行了。有于节点数才100,直接用矩阵存图就可了
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=110;
int n,val[maxn];
int map[maxn][maxn];
int dp[maxn][2*maxn],M;
void dfs(int u,int fa)
{
dp[u][0]=val[u];
int tmp[2*maxn]={0};
for(int v=1;v<=n;v++)
if(map[u][v]&&v!=fa)
{
dfs(v,u);
//cout<<v<<" "<<map[u][v]<<endl;
for(int i=0;i<=M;i++)
for(int j=0;i+j+2*map[u][v]<=M;j++)
tmp[i+j+2*map[u][v]]=max(tmp[i+j+2*map[u][v]],dp[u][i]+dp[v][j]);
for(int i=0;i<=M;i++)
dp[u][i]=max(dp[u][i],tmp[i]),tmp[i]=0;
}
}
int main()
{
int a,b,c;
while(scanf("%d",&n)==1)
{
for(int i=1;i<=n;i++)
scanf("%d",val+i);
memset(map,0,sizeof(map));
memset(dp,0,sizeof(dp));
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&a,&b,&c);
map[a][b]=map[b][a]=c;
}
int start;
scanf("%d%d",&start,&M);
dfs(start,-1);
int ans=0;
for(int i=0;i<=M;i++)
ans=max(ans,dp[start][i]);
printf("%d\n",ans);
}
return 0;
}
PS:
H 题我找到规律了,但是就是wa,为什么结果用我暴力程序的结果对拍都对呀,不懂得为什么wa,泪……
B题 看到好多人都过了,我就是不知道怎么dp,想用搜索水过过去,但是不是wa就是TLE,泪……明明好多人都过了
B题原来就是01背包就行了,很巧妙的样子,想别人要的代码,谢谢别人的代码
#include<iostream>
#include<stdio.h>
#include<stdio.h>
#include<cstring>
using namespace std;
int ti[40];
int wi[40];
int dp[350];
int N,L;
int ma(int a,int b)
{
return a > b ? a: b;
}
int main( )
{
int i,j,max;
while( scanf("%d%d",&N,&L) != EOF )
{
memset(ti,0,sizeof(ti));
memset(wi,0,sizeof(wi));
for( i = 0; i < 350; i++ )
dp[i] = 0;
for( i = 1; i <= N; i++ )
scanf("%d%d",&ti[i],&wi[i]);
max = 999999999;
for( i = 1; i <= N; i++ )
{
for( j = 1; j <= 330; j++ )
{
dp[ti[i]+j] = ma( dp[ti[i]+j],dp[j] + wi[i]*j );
if( dp[j] >= L && max > j )
max = j;
}
}
cout<<max<<endl;
}
//system("pause");
return 0;
}
Treasure Hunt IV
当时比赛的时候没有做出来,隔几天之后再想想发现是个水题呀!原因就是当时虽然也找到规律了,但是没有想好,就一直在哪儿调试……悲剧
规律我都不说了,很容易找到的
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
ll solve(ll n)
{
if(n<0) return 0;
ll ret=(ll)(sqrt(n*1.0)+0.00000001);
ll ans=0;
if(ret%2==0) ans+=n-ret*ret+1,ret--;
ans+=ret*(ret+1)/2;
return ans;
}
int main()
{
ll a,b;
while(scanf("%lld %lld",&a,&b)==2)
{
printf("%lld\n", solve(b) - solve(a-1));
//cout<< solve(b) <<endl;
}
return 0;
}
C 题 Count Path pair 组合数学公式就是( C(m + n, n) * C(m + q - p, q) - C(m + q, q) * C(n + m - p, n)) % 100000007 ,然后利用欧拉函数求逆元即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=100000007;
ll quick_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int main()
{
ll n,m,p,q;
while(scanf("%lld %lld %lld %lld",&m,&n,&p,&q)==4)
{
ll ans1=1,ans2=1,ans3=1,ans4=1;
for(ll i=0;i<n;i++)
ans1=ans1*(m+n-i)%mod;
for(ll i=0;i<q;i++)
ans2=ans2*(m-p+q-i)%mod;
for(ll i=0;i<m;i++)
ans3=ans3*(m+q-i)%mod;
for(ll i=0;i<n;i++)
ans4=ans4*(m-p+n-i)%mod;
ll tmp1=1,tmp2=1,tmp3=1;
for(int i=0;i<n;i++)
tmp1=tmp1*(n-i)%mod;
for(int i=0;i<q;i++)
tmp2=tmp2*(q-i)%mod;
for(int i=0;i<m;i++)
tmp3=tmp3*(m-i)%mod;
ans1=ans1*quick_pow(tmp1,mod-2)%mod;
ans2=ans2*quick_pow(tmp2,mod-2)%mod;
ans3=ans3*quick_pow(tmp3,mod-2)%mod;
ans4=ans4*quick_pow(tmp1,mod-2)%mod;
ll ans=(ans1*ans2%mod - ans3*ans4%mod)%mod;
printf("%lld\n",(ans+mod)%mod);
}
return 0;
}
本文分享了浙大月赛ACM竞赛中部分题目的解题思路及代码实现,包括通过枚举和判断构成的数来解决A题、使用搜索策略完成J题、运用贪心算法和线段树解决K题等。文章还提供了E题的树形DP解决方案,并附带了B题和Treasure Hunt IV题目的高效解法。
1368

被折叠的 条评论
为什么被折叠?



