备战noip week3

POJ2620 Minimal coverage
题意:给出若干条线段,要求选择尽量少的线段使其完全覆盖一段区间。要求输出方案。
题解:首先按照左端点排序。然后扫一遍。枚举所有左端点在当前已覆盖的最右侧之前的线段,统计往右延伸的最大值。然后将这条线段记入答案
证明:首先枚举的这些线段包含且仅包含合法的线段。在所有合法线段中,贪心地想,应该尽量地向右延伸。如果一个向右没有延伸到最大的合法线段在某个最优解中,那么用向右延伸到最大的线段来替代仍然可以得到另一组不劣的解。

#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
const int N=1e5+5;
int m,cnt;
vector<int>ans;
struct Segment
{
	int l,r;
	bool operator<(const Segment& rhs)const{return l<rhs.l;}
}a[N];
int main()
{
	scanf("%d",&m);
	while(1)
	{
		++cnt;
		scanf("%d%d",&a[cnt].l,&a[cnt].r);
		if(!a[cnt].l&&!a[cnt].r){--cnt;break;}
	}
	sort(a+1,a+cnt+1);
	int l=0;
	for(int i=1;i<=cnt&&l<m;++i)
	{
		if(a[i].r<=l)continue;
		if(a[i].l>l){ans.clear();break;}
		int maxr=0,t=0;
		for(int j=i;j<=cnt&&a[j].l<=l;++j)
			if(a[j].r>maxr)maxr=a[j].r,t=j;
		if(maxr<=l){ans.clear();break;}
		ans.push_back(t);
		l=a[t].r;
	}
	if(ans.empty())puts("No solution");
	else
	{
		printf("%d
",(int)ans.size());
		for(size_t i=0;i<ans.size();++i)
			printf("%d %d
",a[ans[i]].l,a[ans[i]].r);
	}
	return 0;
}

HDU1009 FatMouse' Trade
题意:有一个最多装V的背包,有若干物品体积为Vi价值为Wi,可以只取走物品的一部分(但价值与体积之比不变)。求取走物品的最大价值
题解:根据生活常识可知,按照性价比从高到低排序后依次取用即可
证明:现在我们来考虑这样做为什么是对的
考虑将在我们用上述方法求出来在最优解中的某个物品弃去,而换成不在最优解中的物品。显然原物品的性价比更高,即在去掉后空出的这部分体积中用原来的物品一定比换成其他不在最优解中的物品价值更高
注意特判m或n等于0的情况

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1005;
int m,n;
double ans;
struct Food
{
	int j,f;
	bool operator<(const Food& rhs)const{return f*rhs.j<rhs.f*j;}
}a[N];
int main()
{
	while(scanf("%d%d",&m,&n)==2)
	{
		if(m==-1&&n==-1)break;
		for(int i=1;i<=n;++i)
			scanf("%d%d",&a[i].j,&a[i].f);
		if(!m||!n){puts("0.000");continue;}
		sort(a+1,a+n+1);
		ans=0;
		for(int i=1;i<=n&&m;++i)
		{
			if(m>=a[i].f)
			{
				m-=a[i].f;
				ans+=(double)a[i].j;
			}
			else
				ans+=(double)m/a[i].f*a[i].j,m=0;
		}
		printf("%.3lf
",ans);
	}
	return 0;
}

POJ1230 Pass-Muraille
题意:给出一个序列,同时给出一些线段。对于序列上每个非零的数i,要求用至少(w_i)条线段将其覆盖。保证有解。问最少选择多少条线段。
题解&证明:首先对线段按照左端点排序。从左至右遍历序列上每一个非零的数i,选择(w_i)条线段覆盖此位置。与先前的题类似,每一次选择线段右端都尽量向右端延伸以覆盖尽量多的需要被覆盖的位置。如此选择一定不会漏掉最优解,证明同第一题。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=105;
int t,n,k;
int w[N];
bool pd[N];
struct Segment
{
	int l,r;
	bool operator<(const Segment& rhs)const{return l<rhs.l;}
}a[N];
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		fill(w,w+101,0);fill(pd,pd+101,0);
		for(int i=1;i<=n;++i)
		{
			int h1,h2;scanf("%d%d%d%d",&a[i].l,&h1,&a[i].r,&h2);
			if(a[i].l>a[i].r)swap(a[i].l,a[i].r);
			for(int j=a[i].l;j<=a[i].r;++j)++w[j];
		}
		for(int i=0;i<=N-5;++i)w[i]=max(0,w[i]-k);
		sort(a+1,a+n+1);
		int l=-1;
		for(int i=0;i<=N-5;++i)
			if(w[i]){l=i;break;}
		if(l<0){puts("0");continue;}
		int ans=0;
		while(1)
		{
			int t=0;a[t].r=-1;
			for(int j=1;j<=n&&a[j].l<=l;++j)
				if(a[j].r>a[t].r&&!pd[j])t=j;
			++ans;pd[t]=1;
			bool flag=0;
			for(int j=l;j<=a[t].r;++j)
			{
				w[j]=max(w[j]-1,0);
				if(w[j])
				{
					if(!flag)l=j;
					flag=1;
				}
			}
			if(!flag)
				for(int j=a[t].r+1;j<=N-5;++j)
					if(w[j]){l=j,flag=1;break;}
			if(!flag)break;
		}
		printf("%d
",ans);
	}
	return 0;
}

UVa11729 Commando War
题意:有若干任务,每个任务有相应的交代时间Bi和执行时间Ji。在同一时间内,只能交代一个任务,但是可以有多个任务同时被执行。求最小的总时间。
题解:按照执行时间长度由高到低排序依次做即可
证明:考虑在上述的方法所得到的解中交换两个任务(x,y),有以下两种情况:
1.x的执行时间大于y的交代时间与执行时间的和。那么原方案显然更优
2.x的执行时间不大于y的交代时间与执行时间的和。交换后更优等价于Bx+By+Jy<Bx+By+Jx 显然矛盾

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,kase;
struct Task
{
	int b,j;
	bool operator<(const Task& rhs)const{return j>rhs.j;}
}a[N];
int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		for(int i=1;i<=n;++i)
			scanf("%d%d",&a[i].b,&a[i].j);
		sort(a+1,a+n+1);
		int tim=0,st=0;
		for(int i=1;i<=n;++i)
		{
			tim=max(tim,st+a[i].b+a[i].j);
			st+=a[i].b;
		}
		printf("Case %d: %d
",++kase,tim);
	}
	return 0;
}

UVa11491 - Erasing and Winning
题意:一个N位大整数,要求保留其中E位使得剩下的E位组成的数最大
题解&证明:从高位向低位走。在给后面留足位数的情况下选择最大的数字,因为如果这一位不选最大的,后面都达不到最大了

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,d,nxt[N][10];
char ch[N];
int main()
{
	while(scanf("%d%d",&n,&d)==2&&n&&d)
	{
		scanf("%s",ch);fill_n(nxt[n],10,-1);
		for(int i=n-1;i>=0;--i)
			for(int j=0;j<10;++j)
				nxt[i][j]=(ch[i]-'0'==j)?i:nxt[i+1][j];
		int e=n-d,i=0;
		while(i<n&&e)
			for(int j=9;j>=0;--j)
				if(nxt[i][j]!=-1&&n-nxt[i][j]>=e)
				{
					printf("%d",j);i=nxt[i][j]+1;--e;break;
				}
		puts("");
	}
	return 0;
}

UVa1615 Highway
题意:给定平面上n(n≤105)个点和一个值D,要求在x轴上选出尽量少的点,使得对于给定的每个点,都有一个选出的点离它的欧几里德距离不超过D。
题解:枚举每个点。以每个点为圆心,以D为半径画圆和x轴相交形成一条线段,要求在这条线段上至少选择一个点。问题转化为选择尽量少的点使得每条线段中至少一个被选中的点。将所有线段按照右端点从小到大排序,每次取这条线段的右端点作为选中点。若一条线段中已经有点被选中则跳过。
证明:每次选择右端点可以尽可能地覆盖之后的线段,一定不比选择更靠左的点劣

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const double eps=1e-8;
int n;
double L,d;
struct Segment
{
	double l,r;
	bool operator<(const Segment& rhs)const{return r<rhs.r;}
}a[N];
int main()
{
	while(scanf("%lf%lf",&L,&d)==2)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			double x,y;scanf("%lf%lf",&x,&y);
			a[i].l=max(0.0,x-sqrt(d*d-y*y));
			a[i].r=min(L,x+sqrt(d*d-y*y));
		}
		sort(a+1,a+n+1);
		int cnt=0;double preans=-1.0;
		for(int i=1;i<=n;++i)
		{
			if(a[i].l<preans+eps)continue;
			++cnt;preans=a[i].r;
		}
		printf("%d
",cnt);
	}
	return 0;
}

网络(Network, Seoul 2007, LA3902)
题意:
给定一个无根树以及其上的一个染色源。要求再选择尽量少的染色源使得对于每个叶子节点都存在一个染色元使得它们的距离不超过k。k为给定常数。树每条边的边权默认为1
题解:以初始给定的染色源为根使其转化为有根树。对于树上深度<=k的叶子节点均已满足要求。考虑不满足要求中最深的是u,u向根的方向走k步到v。将v作为染色源,再判断哪些点被覆盖。不断重复此过程即可。
证明:其实对于u来说只要向上走<=k步都是可行的。但是越向上走就越有可能覆盖更多的叶子节点。

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int t,s,n,k,fa[N],ans;
bool cov[N];
vector<int>e[N],node[N];
void dfs(int u,int pa,int d)
{
	fa[u]=pa==0?u:pa;
	if(e[u].size()==1&&d>k)
		node[d].push_back(u);
	for(size_t i=0;i<e[u].size();++i)
	{
		int v=e[u][i];
		if(v==pa)continue;
		dfs(v,u,d+1);
	}
}
void dfs2(int u,int pa,int d)
{
	if(d>k)return;
	cov[u]=true;
	for(size_t i=0;i<e[u].size();++i)
	{
		int v=e[u][i];
		if(v!=pa)dfs2(v,u,d+1);
	}
}
inline void solve()
{
	ans=0;
	fill(cov+1,cov+n+1,0);
	for(int d=n;d>k;--d)
		for(size_t i=0;i<node[d].size();++i)
		{
			int u=node[d][i];
			if(cov[u])continue;
			for(int j=1;j<=k;++j)u=fa[u];
			dfs2(u,0,0);
			++ans;
		}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&s,&k);
		for(int i=1;i<=n;++i)e[i].clear(),node[i].clear();
		for(int i=1;i<n;++i)
		{
			int x,y;scanf("%d%d",&x,&y);
			e[x].push_back(y);
			e[y].push_back(x);
		}
		dfs(s,0,0);
		solve();
		printf("%d
",ans);
	}
	return 0;
}

ZOJ1076 Gene Assembly (ACM/ICPC, SouthAmerica 2001)

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n;
vector<int>ans;
struct Segment
{
	int l,r,id;
	bool operator<(const Segment& rhs)const{return r<rhs.r;}
}a[N];
int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		for(int i=1;i<=n;++i)
		{
			scanf("%d%d",&a[i].l,&a[i].r);
			a[i].id=i;
		}
		sort(a+1,a+n+1);
		ans.clear();
		int l=-1;
		for(int i=1;i<=n;++i)
		{
			if(a[i].l<l)continue;
			l=a[i].r;
			ans.push_back(a[i].id);
		}
		for(size_t i=0;i<ans.size()-1;++i)
			printf("%d ",ans[i]);
		printf("%d",ans[ans.size()-1]);
		puts("");
	}
	return 0;
}

勇者斗恶龙(The Dragon of Loowater, UVa11292)

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5;
int n,m,d[N],k[N];
int main()
{
	while(scanf("%d%d",&n,&m)==2&&n&&m)
	{
		for(int i=1;i<=n;++i)scanf("%d",d+i);
		for(int i=1;i<=m;++i)scanf("%d",k+i);
		sort(d+1,d+n+1);sort(k+1,k+m+1);
		int cur=1,ans=0;
		for(int i=1;i<=m&&cur<=n;++i)
			if(k[i]>=d[cur])++cur,ans+=k[i];
		if(cur<=n)puts("Loowater is doomed!");
		else printf("%d
",ans);
	}
	return 0;
}

UVa10954 Add all
同合并果子

#include<bits/stdc++.h>
using namespace std;
int n;
priority_queue<int,vector<int>,greater<int> >pq;
int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		while(!pq.empty())pq.pop();
		for(int i=1;i<=n;++i)
		{
			int x;scanf("%d",&x);
			pq.push(x);
		}
		int ans=0;
		while(pq.size()>1)
		{
			int u=pq.top();pq.pop();
			int v=pq.top();pq.pop();
			ans+=u+v;
			pq.push(u+v);
		}
		printf("%d
",ans);
	}
	return 0;
}

装箱(Bin Packing, SWERC 2005, UVa1149)
最轻的和尽量重的在一起
双指针扫描

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int t,n,l,a[N];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
int main()
{
	t=read();
	while(t--)
	{
		n=read();l=read();
		for(int i=1;i<=n;++i)a[i]=read();
		sort(a+1,a+n+1);
		int i=1,j=n,ans=0;
		for(;;)
		{
			if(a[i]+a[j]>l)++ans,--j;
			else ++ans,++i,--j;
			ans+=(i==j);if(i>=j)break;
		}
		printf("%d
",ans);if(t)puts("");
	}
	return 0;
}

UVa10026 Shoemaker's Problem
本题的贪心十分巧妙
证明的思路是考虑交换两个任务
此坑待补

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int t,n;
struct task
{
	int s,t,id;
	bool operator<(const task& rhs)const
	{
		if(s*rhs.t!=t*rhs.s)return s*rhs.t>t*rhs.s;
		return id<rhs.id;
	}
}a[N];
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%d%d",&a[i].t,&a[i].s),
			a[i].id=i;
		sort(a+1,a+n+1);
		for(int i=1;i<=n;++i)
			printf("%d%s",a[i].id,i==n?"
":" ");
		if(t)puts("");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zmyzmy/p/13709334.html