标签:河南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; }
#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; }
#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算法:
#include<cstdio> #include<string> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int maxn=100010; const int maxm=500010; int a[maxn],b[maxn],head1[maxn],head2[maxn],tot; struct node{ int u,v; int next; }edge[maxm]; void addedge(int u,int v){ edge[tot].u=u; edge[tot].v=v; edge[tot].next=head1[u]; head1[u]=tot++; edge[tot].u=v; edge[tot].v=u; edge[tot].next=head2[v]; head2[v]=tot++; } int spfa(int s,int n){ queue<int> q; int x,i,v; int mark1[maxn],mark2[maxn]; memset(mark1,0,sizeof(mark1)); memset(mark2,0,sizeof(mark2)); mark1[s]=1; mark2[n]=1; q.push(s); while(!q.empty()){ x=q.front(); q.pop(); for(i=head1[x];i!=-1;i=edge[i].next){ v=edge[i].v; a[v]=min(a[v],a[x]); if (!mark1[v]){ mark1[v]=1; q.push(v); } } } q.push(n); while(!q.empty()){ x=q.front(); q.pop(); for(i=head2[x];i!=-1;i=edge[i].next){ v=edge[i].v; b[v]=max(b[v],b[x]); if (!mark2[v]){ mark2[v]=1; q.push(v); } } } int ans=0; for(i=1;i<=n;i++) if (mark1[i]&&mark2[i]) ans=max(ans,b[i]-a[i]); return ans; } int main(){ int n,m,v,u,x,i; while(scanf("%d%d",&n,&m)!=EOF){ for(i=1;i<=n;i++){ scanf("%d",&a[i]); b[i]=a[i]; } tot=0; memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); while(m--){ scanf("%d%d%d",&u,&v,&x); addedge(u,v); if (x==2) addedge(v,u); } printf("%d\n",spfa(1,n)); } return 0; }
E。最经典的二维DP题。初始化第一行第一列,然后dp[i][j]只与dp[i-1][j],dp[i][j-1]有关。dp[i][j]为走到(i,j)方格时的最大值
#include<cstdio> #include<cstring> using namespace std; const int maxn=50; int n,m; int num[maxn][maxn]; int dp[maxn][maxn]; int main(){ //freopen("input.txt","r",stdin); int i,j; while(scanf("%d%d",&n,&m)!=EOF){ for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&num[i][j]); memset(dp,0,sizeof(dp)); dp[1][1]=num[1][1]; for(i=2;i<=m;i++) dp[1][i]=dp[1][i-1]+num[1][i]; for(i=2;i<=n;i++) dp[i][1]=dp[i-1][1]+num[i][1]; //for(i=1;i<=n;i++) // for(j=1;j<=m;j++) // printf("%d%c",dp[i][j],j==m?'\n':' '); for(i=2;i<=n;i++) for(j=2;j<=m;j++) if (dp[i-1][j]>dp[i][j-1]) dp[i][j]=dp[i-1][j]+num[i][j]; else dp[i][j]=dp[i][j-1]+num[i][j]; printf("%d\n",dp[n][m]); } return 0; }
1.是否有单独出现的竞拍价格。若有,选择其中最小的
2.若1不成立,则取最小的其中的竞拍价格,且找到第一个出此竞拍价格的人作为竞拍者
#include<cstdio> #include<cstring> using namespace std; const int maxn=2000; int check[maxn]; char str[maxn][10]; int num[maxn]; int n,m; int getvalid(){ int i,j; for(i=1;i<=n;i++) if (check[i]==1){ for(j=1;j<=m;j++) if (num[j]==i){ printf("The winner is %s\n",str[j]); printf("The price is %d\n",num[j]); } break; } if (i==n+1) return -1; return 1; } int main(){ //freopen("input.txt","r",stdin); int i; while(scanf("%d%d",&n,&m)!=EOF){ memset(check,0,sizeof(check)); for(i=1;i<=m;i++){ scanf("%s%d",str[i],&num[i]); check[num[i]]++; } int ans=getvalid(); if (ans!=-1){ continue; } for(i=1;i<=n;i++) if (check[i]){ for(int j=1;j<=m;j++) if (num[j]==i){ printf("The winner is %s\n",str[j]); printf("The price is %d\n",num[j]); } break; } } return 0; }
题目定义了两个价格容易把人弄晕。其实把根据距离算出的费用与买种子的费用算成一个总体费用当作在该种子站购买种子的费用,然后就发现,可以排序一发从小到大贪心购买即得到最小
#include"iostream" #include"stdio.h" #include"stdlib.h" #include"algorithm" using namespace std; struct station { int price; int count; }st[10000]; int cmp(station x,station y) { if(x.price<y.price) return 1; else return 0; } int main() { // freopen("input.txt","r",stdin); int t1,t2,t3; int k,e,n; int i; while(cin>>k>>e>>n) { for(i=1;i<=n;i++) { scanf("%d%d%d",&t1,&t2,&t3); st[i].price=t3+e-t1; st[i].count=t2; } sort(st+1,st+n+1,cmp); // for(i=1;i<=n;i++) // cout<<st[i].price<<" "<<st[i].count<<endl; int temp=k; int ans=0; for(i=1;i<=n;i++) { if(temp>st[i].count) { ans+=st[i].price*st[i].count; temp-=st[i].count; } else { ans+=temp*st[i].price; break; } } cout<<ans<<endl; } return 0; }
从网上找到了个很好的题解分享给大家:
题解写到这,总结下:
这次比赛感觉是场手速赛,出ABCEFG都不难关键是一开始找到这些题并把握好姿势AC,尽可能1A过,DH这种题并不太难,但是在比赛场上要想到这种方法对于我这种弱弱还是比较难。继续加油
标签:河南acm省赛
原文地址:http://blog.csdn.net/kevin66654/article/details/45476235