task

poj1328【Radar Installation】
题意就是求最少要用多少个半径为 (r) 的圆覆盖所有点,无解输出-1。
这种题的套路就是卡边界,也就是贪心。
首先,把点按 (x) 坐标排序。
对于第一个点,我们可以找到一个最靠右的圆,使得这个点刚好在圆上,设圆心为((a,0))
然后对于新加入的一个点,我们判断点是否被圆 (a) 包含,若不是,对于新点再求一个 (a'),如果 (a'<a) 的话,更新 (a),否则 ans++。
因为按 (x) 排序后,上一个对答案造成贡献的点的 (x) 坐标(<=a'<=)新点的 (x) 坐标

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 1005, inf = 1<<30;

struct poi
{
	int x,y;
	inline bool operator <(const poi &P) const { return x < P.x; }
}p[N];

int main()
{
	freopen("Radar_Installation.in","r",stdin);
	freopen("Radar_Installation.out","w",stdout);
	int n,i,id,sum,r;
	db x,tmp;
	bool fg;
	id=1;
	while (scanf("%d",&n))
		{
			fg=false;
			if (!n)
				break;
			r=gi();
			for (i=1; i<=n; ++i)
				p[i].x=gi(), p[i].y=gi();
			for (i=1; i<=n; ++i)
				if (p[i].y > r)
					{
						printf("Case %d: %d
",id++,-1);
						fg=true;
						break;
					}
			if (fg)
				continue;
			sum=0, r*=r;
			sort(p+1,p+n+1);
			sum++;
			x=p[1].x+sqrt(r-p[1].y*p[1].y);
			for (i=2; i<=n; ++i)
				{
					if ((p[i].x-x)*(p[i].x-x)+p[i].y*p[i].y <= r)
						continue;
					tmp=p[i].x+sqrt(r-p[i].y*p[i].y);
					if (tmp > x)
						sum++;
					x=tmp;
				}
			printf("Case %d: %d
",id++,sum);
		}
	return 0;
}

poj3253【Fence Repair】
题目要求把一块木板锯成 (n) 段,每次花费的代价是锯之前的长度。求最小代价。
实际上这道题如果想到了倒过来做的话,就很水了。(但是并没有想到)
把一块木板锯成 (n) 段可以看成 (n) 段木板合成一段,每次代价是合并的两段长度之和。
于是就变成了合并果子。
碰到这种阶段性很强的题,可以考虑倒过来做。这个与一些题目中把删点变成加点来做是一样的套路。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 2e4+5, inf = 1<<30;

priority_queue <int> q;

int main()
{
	freopen("Fence_Repair.in","r",stdin);
	freopen("Fence_Repair.out","w",stdout);
	int n,i,x;
	ll ans;
	n=gi();
	for (i=1; i<=n; ++i)
		x=gi(), q.push(-x);
	ans=0;
	while (true)
		{
			x=q.top(), q.pop();
			x+=q.top(), q.pop(), ans-=x;
			if (q.empty())
				break;
			q.push(x);
		}
	printf("%lld
",ans);
	return 0;
}

poj3662 【Telephone Lines】
题意是求 (1)(n) 的第 (k+1) 大的边最小。
和最大边最小一样,二分答案,然后跑 (spfa),算出 (1~n) 最少要经过多少条大于 (mid) 的边。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 1005, M = 2e4+5, inf = 1<<30;

int n,m,k,mid,num,f[N],to[M],len[M],nx[M],dis[N],E[M];
bool vis[N];
queue <int> q;

inline void add(int x,int y,int z)
{
	to[++num]=y, len[num]=z, nx[num]=f[x], f[x]=num;
}

inline bool spfa()
{
	int i,o,et,ed;
	for (i=2; i<=n; ++i)
		dis[i]=inf, vis[i]=false;
	vis[1]=true, q.push(1);
	while (!q.empty())
		{
			o=q.front(), q.pop(), vis[o]=false;
			for (i=f[o]; i; i=nx[i])
				{
					et=to[i], ed=len[i];
					if (dis[et] > dis[o]+(ed > E[mid]))
						{
							dis[et]=dis[o]+(ed > E[mid]);
							if (!vis[et])
								q.push(et), vis[et]=true;
						}
				}
		}
	return dis[n] <= k;
}

int main()
{
	freopen("Telephone_Lines.in","r",stdin);
	freopen("Telephone_Lines.out","w",stdout);
	n=gi(), m=gi(), k=gi();
	int i,x,y,z;
	for (i=1; i<=m; ++i)
		x=gi(), y=gi(), z=gi(), add(x,y,z), add(y,x,z), E[i]=z;
	sort(E+1,E+m+1);
	E[m+1]=-1, x=0, y=m+1;  //trick
	while (x <= y)
		{
			mid=(x+y)>>1;
			if (spfa())
				y=mid-1;
			else
				x=mid+1;
		}
	printf("%d
",E[min(y+1,m+1)]);
	return 0;
}

poj3666【Making the Grade】
题意是求把一个数列变成不增或不降的最小代价,但数据好像只要求变成不降的代价。
首先,一个显然的结论就是在代价不变的前提下,一定可以满足修改后的数列最大值是原数列中的某一个数。想到这一点就可以做了。
(f[i][j]) 表示已经处理好前 (i) 位且第 (i) 位为 (j) 的最小代价。因为第 (i) 位已经是 (j) ,所以 (f[i][j]) 可以从 (f[i-1][k]) ((k <= j)) 转移转移过来。但这样是 (O(N^3)) ,所以要记 (f[i-1]) 的最小值。因为 (f[i][j]) 是递推的,所以可以边推边记。
然后就是 (j) 可能很大,但 (n) 很小,所以要离散。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 2005, inf = 1<<30;

int a[N],b[N];
ll f[N][N];

int main()
{
	freopen("Making_the_Grade.in","r",stdin);
	freopen("Making_the_Grade.out","w",stdout);
	int n,i,j;
	ll m,ans;
	n=gi();
	for (i=1; i<=n; ++i)
		b[i]=a[i]=gi();
	sort(b+1,b+n+1);
	for (i=1; i<=n; ++i)
		{
			m=inf;
			for (j=1; j<=n; ++j)
				m=min(m,f[i-1][j]), f[i][j]=m+abs(b[j]-a[i]);
		}
	ans=inf;
	for (i=1; i<=n; ++i)
		ans=min(ans,f[n][i]);
	printf("%lld
",ans);
	return 0;
}

poj3616【Milking Time】
题意是给你一些区间,每个区间有一些价值,若两个区间间隔不超过 (r),这两个区间不能同时选。
这道题的转移和最长上升子序列差不多,也是从前面的合法状态中取 (max) 加上当前的价值。要注意的是每个点的初始值是自身的价值而不是0。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 1005, inf = 1<<30;

int f[N];
struct range
{
	int l,r,val;
	inline bool operator <(const range &R) const { return l < R.l; }
}ra[N];

int main()
{
	freopen("Milking_Time.in","r",stdin);
	freopen("Milking_Time.out","w",stdout);
	RG int n,m,r,i,j,ans;
	n=gi(), m=gi(), r=gi();
	for (i=1; i<=m; ++i)
		ra[i].l=gi(), ra[i].r=gi(), ra[i].val=gi();
	sort(ra+1,ra+m+1);
	for (i=1; i<=m; ++i)
		f[i]=ra[i].val;
	ans=f[1];
	for (i=2; i<=m; ++i)
		{
			for (j=1; j<i; ++j)
				if (ra[j].r <= ra[i].l-r)
					f[i]=max(f[i],f[j]+ra[i].val);
			ans=max(ans,f[i]);
		}
	printf("%d
",ans);
	return 0;
}

poj3280【Cheapest Palindrome】
题意是给你一个字符串,求把他变成回文串的最小代价。修改不同的字符有不同的代价。
做这到题的时候我不知道在干嘛,完全没有思路,一些很显然的东西也没有看出来。
首先,对于一个字符,加入和删除本质上是一样的。因为对于一个串,两端不同的话,既可以删除一个不同的字符,又可以添加一个相同的字符。
然后,就可以像普通的区间dp一样更新了。
(f[i][j]) 表示从i到j这一段变成回文的最小代价。$f[i][j] $可以从 (f[i+1][j]) 转移过来,也可以从 (f[i][j-1]) 转移过来。这两个转移是不需要条件的,还有一个就是当 (a_{i}==a_{j}) 时,(f[i][j]) 可以从 (f[i+1][j-1]) 转移过来。
然后就是拓扑序的问题,这道题更新 (f[i][j]) 要用到 (f[i+1])(f[i][j-1]) 的值,所以 (i) 要从大往小枚举,(j) 要从小往大枚举。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 2005, inf = 1<<30;

int cst[30],f[N][N];
char a[N];

int main()
{
	freopen("Cheapest_Palindrome.in","r",stdin);
	freopen("Cheapest_Palindrome.out","w",stdout);
	int n,m,i,j;
	char s;
	n=gi(), m=gi();
	scanf("%s",a+1);
	for (i=1; i<=n; ++i)
		{
			s=getchar();
			while (s < 'a' || s > 'z')
				s=getchar();
			cst[s-'a']=min(gi(),gi());
		}
	for (i=m-1; i; --i)
		for (j=i+1; j<=m; ++j)
			{
				f[i][j]=min(f[i+1][j]+cst[a[i]-'a'],f[i][j-1]+cst[a[j]-'a']);
				if (a[i] == a[j])
					f[i][j]=min(f[i][j],f[i+1][j-1]);
			}
	printf("%d
",f[1][m]);
	return 0;
}
原文地址:https://www.cnblogs.com/y142857/p/7499321.html