• 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;
    }
    
  • 相关阅读:
    常见排序算法-----堆排序
    深度优先搜索和广度优先搜索
    剑指offer整理-------二维数组查找
    常见排序算法-----希尔排序
    log4j日志不能输出到文件
    常见排序算法-----直接插入排序
    从零开始学习springBoot(使用thymeleaf和freemarker模板)
    从零开始学习springBoot(Spring Boot使用Druid和监控配置)
    从零开始学习springBoot(定时任务)
    从零开始学习springBoot(默认静态资源和自定义资源映射)
  • 原文地址:https://www.cnblogs.com/y142857/p/7499321.html
Copyright © 2020-2023  润新知