河南省第三届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算法:

#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;
}

另外,在网上还可以搜到一种强连通缩点之后继续构造新图进行dfs搜索的,本人觉得那种方法必须利用模板,没有Spfa好写,没有在此贴出

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;
}

F。读题觉得有点费劲。其实翻译过来很好懂,就是个定义了个物品竞拍的原则,相当于个水模拟:

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;
}

G。题目废话太多。。其实是个水贪心题

题目定义了两个价格容易把人弄晕。其实把根据距离算出的费用与买种子的费用算成一个总体费用当作在该种子站购买种子的费用,然后就发现,可以排序一发从小到大贪心购买即得到最小

#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;
}

H。看到区间分配问题,立马想到线段树。。但是这个,我不太会。

从网上找到了个很好的题解分享给大家:

戳我学一发线段树姿势

题解写到这,总结下:

这次比赛感觉是场手速赛,出ABCEFG都不难关键是一开始找到这些题并把握好姿势AC,尽可能1A过,DH这种题并不太难,但是在比赛场上要想到这种方法对于我这种弱弱还是比较难。继续加油

时间: 2024-08-03 18:33:53

河南省第三届ACM程序设计大赛题解的相关文章

nyoj 1238 最少换乘 (河南省第八届acm程序设计大赛)

题目1238 题目信息 运行结果 本题排行 讨论区 最少换乘 时间限制:2000 ms  |  内存限制:65535 KB 难度:3 描述 欧洲某城是一个著名的旅游胜地,每年都有成千上万的人前来观光旅行.Dr. Kong决定利用暑假好好游览一番.. 年轻人旅游不怕辛苦,不怕劳累,只要费用低就行.但Dr. Kong年过半百,他希望乘坐BUS从住的宾馆到想去游览的景点,期间尽可量地少换乘车. Dr. Kon买了一张旅游地图.他发现,市政部门为了方便游客,在各个旅游景点及宾馆,饭店等地方都设置了一些公

nyoj1237 最大岛屿(河南省第八届acm程序设计大赛)

题目1237 题目信息 运行结果 本题排行 讨论区 最大岛屿 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述 神秘的海洋,惊险的探险之路,打捞海底宝藏,激烈的海战,海盗劫富等等.加勒比海盗,你知道吧?杰克船长驾驶着自己的的战船黑珍珠1号要征服各个海岛的海盜,最后成为海盗王.  这是一个由海洋.岛屿和海盗组成的危险世界.面对危险重重的海洋与诡谲的对手,如何凭借智慧与运气,建立起一个强大的海盗帝国. 杰克船长手头有一张整个海域的海图,上面密密麻麻分布着各个海屿的位置及面

nyoj 1239 引水工程 (河南省第八届acm程序设计大赛)

题目1239 题目信息 运行结果 本题排行 讨论区 引水工程 时间限制:2000 ms  |  内存限制:65535 KB 难度:3 描述 南水北调工程是优化水资源配置.促进区域协调发展的基础性工程,是新中国成立以来投资额最大.涉及面最广的战略性工程,事关中华民族长远发展."南水北调工程",旨在缓解中国华北和西北地区水资源短缺的国家战略性工程.就是把中国长江流域丰盈的水资源抽调一部分送到华北和西北地区.我国南涝北旱,南水北调工程通过跨流域的水资源合理配置,促进南北方经济.社会与人口.资

nyoj 1239 引水project (河南省第八届acm程序设计大赛)

题目1239 pid=1239" style="color:rgb(55,119,188)">题目信息 pid=1239" style="color:rgb(55,119,188)">执行结果 pid=1239" style="color:rgb(55,119,188)">本题排行 讨论区 引水project 时间限制:2000 ms  |  内存限制:65535 KB 难度:3 描写叙述 南水北调

河南省第五届ACM程序设计大赛

D:   遥 控 器 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<map> using namespace std; typedef unsigned long long LL; #define

河南省第八届ACM程序设计大赛

A:挑战密室 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <stack> #include <map> #include <vector> #include <queue> using namespace std; typedef lon

河南省第八届ACM程序设计大赛总结

简单的对这次省赛做个总结:总体来说这个比赛过程中做的还算比较顺利,虽然中间多多少少遇到一些坑,正式比赛开始后,我们就开始找水题,当然了我首先把英文题目翻译了一遍,发现了一道水题,这道题目其实就是判断点在二维平面坐标系中的象限位置,然后果断交给了zy去做,此时wx已经A了一道题了,至于什么情况我并不知情,然后又发现了一道较水的英文题目,是遍历连续数组子串求最大平均值的问题,我们商量好思路后,然后又果断交给了zy去做,之后交了一发,果断Wrong了,然后又看了看题目要求,好吧,输出的最后一句单词没有

河南省第七届ACM程序设计大赛赛后总结

我们学校ACM集训开始于4月5号(清明节),5月25日在郑州解放军信息工程大学举办,集训耗时50天,但是感觉效果还行,但是也不是太好:我们也已经尽力了,虽然说只拿了个银牌,每份收获的背后,都会有辛勤的汗水,毕竟我们也努力了: 下面说说比赛中的问题:觉得吧,虽说模拟赛参加了很多,但是到了正式的比赛的时候,还是出现了紧张的情况,九点开始比赛,我们AC第一道题的时间是在十点半左右,题目不难而且曾经也做过啊,但是就是调试不好,越调试不好,心里就越急啊:刚开始看题的时间,我们都看了第一道题,意思也都明白,

河南省第三届acm省赛 AMAZING AUCTION

AMAZING AUCTION 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 Recently the auction house has introduced a new type of auction, the lowest price auction. In this new system, people compete for the lowest bid price, as opposed to what they did in the past. Wh