河南省第三届ACM程序设计大赛题解

光说不练是假把式,先给各大巨巨们一个刷题链接:戳我进入刷题OJ

这届比赛水题有点多,想拿奖保证好手速即可。但是想拿高名次并不太容易

A。常规做法我不太会,但是根据题目的数据发现,可以开个这么大的数组哈哈,那么万能的暴力保证1A。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=500;int num[maxn];int main(){//freopen("input.txt","r",stdin);int n;int a,b,c;while(scanf("%d",&n)!=EOF){memset(num,0,sizeof(num));while(n–){scanf("%d%d%d",&a,&b,&c);for(int i=b;i<b+c;i++)num[i]+=a;}int ans=0;for(int i=1;i<=180;i++)ans=max(ans,num[i]);printf("%d\n",ans);}return 0;}B。简单题,比A更好做。O(sqrt(n))判断质数即可。关键是注意同等距离的时候取比n大的那个素数

#include<cstdio>#include<cstring>using namespace std;int n;int is_prime(int n){for(int i=2;i*i<=n;i++)if (n%i==0) return 0;return 1;}int main(){//freopen("input.txt","r",stdin);int i,k;scanf("%d",&n);while(n–){scanf("%d",&k);for(i=0;;i++){if (is_prime(k+i)){printf("%d\n",k+i);break;}else if (is_prime(k-i)){printf("%d\n",k-i);break;}}}return 0;}C。这个题一开始把我吓住了,比赛时候用bin神模板的强联通缩点化图为树得到多少个叶子节点,然后ans=(叶子节点数+1)/2。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=10010;const int maxm=20010;struct Edge{int to,next;bool cut;}edge[maxm];int head[maxn],tot;int low[maxn],dfn[maxn],stack[maxn],belong[maxn];int Index,top;int block;bool instack[maxn];int bridge;void addedge(int u,int v){edge[tot].to=v;edge[tot].next=head[u];edge[tot].cut=false;head[u]=tot++;}void Tarjan(int u,int pre){int v;low[u]=dfn[u]=++Index;stack[top++]=u;instack[u]=true;for(int i=head[u];i!=-1;i=edge[i].next){v=edge[i].to;if (v==pre) continue;if (!dfn[v]){Tarjan(v,u);if (low[u]>low[v]) low[u]=low[v];if (low[v]>dfn[u]){bridge++;edge[i].cut=true;edge[i^1].cut=true;}}else if (instack[v]&&low[u]>dfn[v]) low[u]=dfn[v];}if (low[u]==dfn[u]){block++;do{v=stack[–top];instack[v]=false;belong[v]=block;}while(v!=u);}}void init(){tot=0;memset(head,-1,sizeof(head));}int du[maxn];void solve(int n){memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(stack,0,sizeof(stack));memset(belong,0,sizeof(belong));memset(instack,false,sizeof(instack));Index=top=block=0;Tarjan(1,0);int ans=0;memset(du,0,sizeof(du));for(int i=1;i<=n;i++)for(int j=head[i];j!=-1;j=edge[j].next)if (edge[j].cut)du[belong[i]]++;for(int i=1;i<=block;i++)if (du[i]==1) ans++;printf("%d\n",(ans+1)/2);}int main(){//freopen("input.txt","r",stdin);int n,u,v,i;while(scanf("%d",&n)!=EOF){init();for(i=1;i<n;i++){scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);}solve(n);}return 0;}关键点:有更简单的方法,因为题中说明了n个点,n-1条边,那么一定构成的是一棵树,所以只需要统计每个节点的度数,最终答案为(度数为1的节点个数+1)/2

D。图论题

先说说自己的错误想法。由于题中说明的是只能买卖一次求最大,那么我能够到的点取最小和最大作差就是最终答案。

因此跑树形DP,从起点1跑一次,,1到其他点的最大最小,从终点n跑一次,n到其他点的最大最小。当时代码写得没有问题,贴出来如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=100050;const int maxm=500050;struct node{int u,v;int next;}edge[maxm];int num[maxn];int dp[maxn];int head[maxn];int tot,ans;bool vis[maxn];void addedge(int u,int v){ edge[tot].u=u; edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++;}void GetMin(int u){vis[u]=true;int tempmax;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v,flag=0;if (vis[v]) continue;GetMin(v);dp[u]=max(dp[u],dp[v]);}dp[u]=max(dp[u],num[u]);}void GetMax(int u){vis[u]=true;int tempmin;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v,flag=0;if (vis[v]) continue;GetMin(v);dp[u]=min(dp[u],dp[v]);}dp[u]=min(dp[u],num[u]);}int main(){freopen("input.txt","r",stdin);int n,m,i;int a,b,c;while(scanf("%d%d",&n,&m)!=EOF){memset(head,-1,sizeof(head));memset(num,0,sizeof(num));ans=tot=0;for(i=1;i<=n;i++) scanf("%d",&num[i]);for(i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);if (c==1){addedge(a,b);}else{addedge(a,b);addedge(b,a);}}memset(vis,0,sizeof(vis));memset(dp,0,sizeof(dp));GetMax(n);for(i=1;i<=n;i++) printf("%d%c",dp[i],i==n?'\n':' ');memset(vis,0,sizeof(vis));for(i=1;i<=n;i++) dp[i]=1000000;GetMax(1);for(i=1;i<=n;i++) printf("%d%c",dp[i],i==n?'\n':' ');printf("%d\n",ans);}return 0;}错误原因:处理不了图中的回路问题

其实我的思路已经跟题解比较接近了,一是贪心找最大最小,但是是从起点能到的点找最小,从能到终点的点找最大。二是必须能够处理回路和权值,Spfa和Bellman-fold都是好选择。

Spfa算法:

走过一个又一个陌生的城市,去感受旖旎的自然风光,

河南省第三届ACM程序设计大赛题解

相关文章:

你感兴趣的文章:

标签云: