[JSOI2010]解题报告+2010~2011小结

[JSOI2010~2011]小结

有一些题目还没写,大概就是考了DP,一些数据结构,还有组合数学和图论这些东西,其实主要是思维难度比较大,比如挖宝藏的问题转化和怎么简便的转移,这些都要在平时多训练,提高思维能力

[JSOI2010]旅行

题解

[JSOI2010]找零钱的洁癖

乱搞,我写的是bfs+贪心

#include<bits/stdc++.h>
#define int long long
using namespace std;
int x,n,a[53],an[500003],num[500003];
map<int,int>mp;
int ask1()
{
	int sum=x,ans=0;
	for(int i=n;i>=1;i--)
		if(a[i]!=0)
			ans+=sum/a[i],sum-=sum/a[i]*a[i];
	if(sum!=0)
		return 1e18;
	return ans;
}
int ask2()
{
	int hd=0,tl=1;
	while(hd<tl)
	{
		hd++;
		for(int i=1;i<=n;i++)
		{
			tl++,an[tl]=an[hd]+1;
			if(tl>=200000)
				return 1e18;
			if(num[hd]<x)
				num[tl]=num[hd]+a[i];
			else
				num[tl]=num[hd]-a[i];
			if(num[tl]==x)
				return an[tl];
			if(mp[num[tl]]==1)
				tl--;
			mp[num[tl]]=1;
		}
	}
	return 1e18;
}
signed main()
{
	scanf("%lld",&x);
	while(scanf("%lld",&a[n+1])!=EOF)
		n++;
	sort(a+1,a+n+1);
	mp[0]=1;
	if(x==0)
		cout<<0;
	else
		cout<<min(ask1(),ask2());
	return 0;
}

[JSOI2010]挖宝藏

我们可以先把问题转化一下,发现要挖到某一个宝藏,实际上就是往上取一个倒三角,那么我们可以把每个点都投影到一个区间上去,而且每一个区间都对应唯一的一个点

然后设\(dp[i]\)表示必选第\(i\)个区间的最大收益,考虑如果选了\(i\)区间,那么被完全包含的区间是必须要选的,直接把这些区间的收益算到\(i\)里面,往前枚举\(j\)转移,如果无交集就直接转移,如果有但是不是包含关系就要暴力去掉他们的交集,如果有包含关系就直接跳过

然后注意一下细节就好了

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int l1,r1,p1,p2,bl;
}a[1003];
int n,x,y,now,sum,ans,dp[1003];
int ask(int l,int r)
{
	return (r-l+2)*(r-l+2)/4;
}
int cmp(node nx,node ny)
{
	if(nx.r1!=ny.r1)
		return nx.r1<ny.r1;
	return nx.l1<ny.l1;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&x,&y,&a[i].p1);
		a[i].l1=x+y+1,a[i].r1=x-y-1,a[i].p2=0,a[i].bl=ask(a[i].l1,a[i].r1);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(a[j].l1>=a[i].l1&&a[j].r1<=a[i].r1)
				a[i].p2+=a[j].p1;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		dp[i]=a[i].p2-a[i].bl,now=1,sum=0;		
		for(int j=1;j<i;j++)
		{
			if(a[j].r1<a[i].l1)
				dp[i]=max(dp[i],dp[j]+a[i].p2-a[i].bl);
			if(a[j].l1<a[i].l1&&a[j].r1>=a[i].l1)
			{
				while(now<=i&&a[now].r1<=a[j].r1)
				{
					if(a[now].l1>=a[i].l1)
						sum+=a[now].p1;
					now++;
				}
				dp[i]=max(dp[i],dp[j]+a[i].p2-a[i].bl-(sum-ask(a[i].l1,a[j].r1)));
			}
		}
		ans=max(ans,dp[i]);
	}
	cout<<ans;
	return 0;
}

[JSOI2010]排名

手玩了好久才搞出了策略......round3的题目好毒瘤啊,这道题还算是相对简单的

可以很显然的发现,这道题需要拓扑排序,第二问很简单,每次取能取到的,最大的就行了,用堆维护一下即可

第二问稍微复杂一丢丢,我的做法是先DFS一遍,预处理这个点往后扩展的最小的点,设点\(x\)\(minn[x]\),然后每次取能取到的,\(minn[x]\)最小的就行了,也是用堆维护

#include<bits/stdc++.h>
using namespace std;
int n,a[200003],chu[200003],minn[200003],num[200003];
vector<int>l[200003];
struct node
{
	int x;
	bool operator < (const node &nx)const
	{
		return minn[nx.x]<minn[x];
	}
}p,txt;
priority_queue<node>q;
struct node1
{
	int x1;
	bool operator < (const node1 &nx)const
	{
		return nx.x1>x1;
	}
}p1,txt1;
priority_queue<node1>q1;
void dfs(int x)
{
	for(int j=0;j<l[x].size();j++)
	{
		dfs(l[x][j]);
		minn[x]=min(minn[x],minn[l[x][j]]);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]!=0)
			l[a[i]].push_back(i),chu[i]++;
		minn[i]=i;
	}
	for(int i=1;i<=n;i++)
		if(chu[i]==0)
		{
			dfs(i);
			p.x=i,p1.x1=i,q.push(p),q1.push(p1);
		}
	for(int i=1;i<=n;i++)
	{
		p=q.top(),q.pop(),num[p.x]=i;
		for(int j=0;j<l[p.x].size();j++)
			txt.x=l[p.x][j],q.push(txt);
	}
	for(int i=1;i<=n;i++)
		cout<<num[i]<<" ";
	cout<<endl;
	for(int i=1;i<=n;i++)
	{
		p1=q1.top(),q1.pop(),num[p1.x1]=i;
		for(int j=0;j<l[p1.x1].size();j++)
			txt1.x1=l[p1.x1][j],q1.push(txt1);
	}
	for(int i=1;i<=n;i++)
		cout<<num[i]<<" ";
	cout<<endl;
	return 0;
}

posted @ 2020-01-31 23:15  dz_ice  阅读(158)  评论(0编辑  收藏  举报