算法协会寒假训练Five(9/11)

BZOJ 1218 [HNOI2003]激光炸弹

 

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(N<=10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。 

Input

输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示xi,yi,vi

Output

输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。

Sample Input

2 1 0 0 1 1 1 1

Sample Output

1

 

题意:给出n个点,每个点有一个价值,问一个边长为r的点最大能获得多大价值。(边界无效)
维护一个二维前缀和即可。

参考原文链接:https://blog.csdn.net/lzr010506/article/details/50552554

注意避开边上点,解法之巧妙!  %
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int n,r,x,y,v;
int dp[5005][5005]; 
int main() {
	scanf("%d%d",&n,&r);
	for(int i=0;i<n;i++){
		scanf("%d%d%d",&x,&y,&v);
		dp[x+1][y+1]+=v;   //相当于该格子值是v
	}
	for(int i=1;i<5005;i++)
		for(int j=1;j<5005;j++)
			dp[i][j]+=dp[i-1][j];
	for(int i=1;i<5005;i++)
		for(int j=1;j<5005;j++)
			dp[i][j]+=dp[i][j-1];
	int ans=-0x3f3f3f3f;      
	for(int i=r;i<5005;i++)    //注意循环起点
		for(int j=r;j<5005;j++)
			ans=max(ans,dp[i][j]-dp[i-r][j]-dp[i][j-r]+dp[i-r][j-r]);
	cout<<ans;
	return 0;
}

 

 

 POJ 3263 Tallest Cow

 

题意:给出n头牛的身高,和m对关系(a[i]与b[i]可以相互看见。即他们中间的牛都比他们矮)。已知最高的牛为第p头,身高为h。求每头牛的身高最大可能是多少。

思路:计算牛的相对大小关系
第p头最高h,比他矮的最高一定是h-1,所以最后每头牛的身高就是,它比p小多少’+h。(因为从左向右累加,所以一定要同时+1,应为前一次-1的判断会影响他自身的身高)
直接减就好了,最后c[p]一定是0,(因为他是最高的嘛)
a[i]与b[i]相互看见==他们中间的牛(a[i+1]到b[i-1])身高都比他们矮,于是把他们身高都减去1即可。关于区间修改,维护差分数列。
注意:输入数据可能有重复以及可能位置顺序不对。

#include<iostream>
#include<cstdio> 
#include<algorithm>
using namespace std;
struct node{
	int a,b;
}p[10005];
int ans[10005];
bool cmp(node x,node y){
	if(x.a!=y.a) return x.a<y.a;
	return x.b<y.b;
}
int main(){
	int n,i,h,r;
	cin>>n>>i>>h>>r;
	for(int i=1;i<=r;i++){
		scanf("%d%d",&p[i].a,&p[i].b);
		if(p[i].a>p[i].b) swap(p[i].a,p[i].b);
	}
	sort(p+1,p+1+r,cmp);
	for(int i=1;i<=r;i++){
		if(p[i].a==p[i-1].a&&p[i].b==p[i-1].b) continue;
		ans[p[i].a+1]--,ans[p[i].b]++; 
	}
	int sum=0;
	for(int i=1;i<=n;i++){
		sum+=ans[i];
		cout<<h+sum<<endl;		
	}
	return 0;
}

 

 

 HDU 对顶栈

 

题目描述:维护一个整数序列的编辑器,有以下五种操作:

I x: 光标前插入x这个数,插入以后光标移动到 x 之后

D: 删除光标前的数

L: 光标左移一位 若已到最左边移不动,就不移

R: 光标右移一位 若移不动,就不移了

Q k: 输出在位置 k 之前的最大前缀和。

思路:建立两个栈,栈 before 存储当前光标位置之前的序列,栈after存储当前位置到序列结尾的序列。用一个数组 dp维护栈 before 的最大前缀和, sum 表示栈A的前缀和数组。

注意:多组数据读入,栈清空

#include<iostream>
#include<cstdio>
#include<string>
#include<stack>
using namespace std;
const int MAX=1e6+5;
int n,x,len;
char com[5];
stack<int>before,after;
int sum[MAX],dp[MAX];
void init() {
	while(!before.empty()) before.pop();
	while(!after.empty()) after.pop();
	len=0;
	sum[0]=0;
	dp[0]=-0x3f3f3f3f;
}
int main() {
	while(~scanf("%d",&n)) {
		init();
		while(n--) {
			scanf("%s",com);
			if(com[0]=='I') {
				scanf("%d",&x);
				before.push(x);
				sum[++len]=sum[len-1]+x;
				dp[len]=max(dp[len-1],sum[len]);
			} else if(com[0]=='D'&&len) {
				before.pop();
				len--;
			} else if(com[0]=='L'&&len) {
				after.push(before.top());
				before.pop();
				len--;
			} else if(com[0]=='R'&&after.size()) {
				before.push(after.top());
				after.pop();
				sum[++len]=sum[len-1]+before.top();
				dp[len]=max(dp[len-1],sum[len]);
			} else if(com[0]=='Q'){
				scanf("%d",&x);
				printf("%d\n",dp[x]);
			}
		}
	}
	return 0;
}

 

 

HDU 6330 Problem L. Visual Cube(模拟) 

 

Little Q likes solving math problems very much. Unluckily, however, he does not have good spatial ability. Everytime he meets a 3D geometry problem, he will struggle to draw a picture.
Now he meets a 3D geometry problem again. This time, he doesn't want to struggle any more. As a result, he turns to you for help.
Given a cube with length aa, width bb and height cc, please write a program to display the cube.

Input

The first line of the input contains an integer T(1≤T≤50)T(1≤T≤50), denoting the number of test cases.
In each test case, there are 33 integers a,b,c(1≤a,b,c≤20)a,b,c(1≤a,b,c≤20), denoting the size of the cube.

Output

For each test case, print several lines to display the cube. See the sample output for details.

Sample Input

2
1 1 1
6 2 4

Sample Output

..+-+
././|
+-+.+
|.|/.
+-+..
....+-+-+-+-+-+-+
.../././././././|
..+-+-+-+-+-+-+.+
./././././././|/|
+-+-+-+-+-+-+.+.+
|.|.|.|.|.|.|/|/|
+-+-+-+-+-+-+.+.+
|.|.|.|.|.|.|/|/|
+-+-+-+-+-+-+.+.+
|.|.|.|.|.|.|/|/.
+-+-+-+-+-+-+.+..
|.|.|.|.|.|.|/...
+-+-+-+-+-+-+....

思路:先解决左上和右下的两个直角三角形,然后解决顶边和正面两面,剩下的就填补一下就行了。

大佬原文链接:https://blog.csdn.net/feizaoSYUACM/article/details/81589870

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
char mp[1005][1005],p[]={'+','-','/','.','|'};
void Print(char c1,char c2,int len,int i,int j){
	for(int k=0;k<len;k++)
		mp[i][j++]=(k%2)?c2:c1;
} 
int main(){
	int t,a,b,c,h;
	cin>>t;
	while(t--){
		memset(mp,'a',sizeof mp);
		cin>>a>>b>>c;
		for(int i=0,cnt=2*b;i<2*b;i++,cnt--){
			Print(p[3],p[3],cnt,i,0);//左上角由'.'组成的直角三角形 
			i%2?Print(p[2],p[3],2*a,i,cnt):Print(p[0],p[1],2*a,i,cnt);//长方形的上底面 
			Print(p[3],p[3],cnt,2*b+2*c-i,2*a+2*b-cnt+1);//右下角由'.'组成的直角三角形 
		} 
		for(int i=2*b;i<2*b+2*c+1;i++)//长方形的正面 
			i%2?Print(p[4],p[3],2*a,i,0):Print(p[0],p[1],2*a,i,0);
		for(int i=0;i<=2*b+2*c;i++)
			for(int j=0;j<=2*a+2*b;j++)
				if(mp[i][j]=='a')//剩下分四种情况填补 
					i%2?(mp[i][j]=(j%2)?p[2]:p[4]):(mp[i][j]=(j%2)?p[3]:p[0]); 
		for(int i=0;i<=2*b+2*c;i++){//输出 
			for(int j=0;j<=2*a+2*b;j++)
				cout<<mp[i][j];
			cout<<endl;
		}
	}
	return 0;
}

 

 

POJ Sudoku 数独填数

 

Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3x3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3x3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task.


Input

The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0.

Output

For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them.

Sample Input

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int pos[85][2];  //记录空格的位置 
int mp[10][10];  //记录九宫格分布 
int judge(int x,int y,int num) { 
	for(int i=0; i<9; i++) {  //同一行同一列九宫格数字不重复 
		if(mp[i][y]==num) return 0;
		if(mp[x][i]==num) return 0;
	}
	int xx=(x/3)*3;  //定位九宫格的起始位置,妙! 
	int yy=(y/3)*3;
	for(int i=0; i<3; i++)
	for(int j=0; j<3; j++)
		if(mp[xx+i][yy+j]==num) return 0;
	return 1;
}
int dfs(int id) {   //对空格处从后往前深搜 
	if(id<0) return 1; //递归出口 
	int x,y;
	for(int num=1; num<=9; num++) { //对九个数字依次“试探” 
		x=pos[id][0],y=pos[id][1]; 
		if(judge(x,y,num)) {
			mp[x][y]=num;   //归档 
			if(dfs(id-1)) return 1;
			mp[x][y]=0;		//尝试下一个数 
		}
	}
	return 0;
}
int main() {
	int t;
	char ch;
	cin>>t;
	while(t--) {
		int num=0;
		for(int i=0; i<9; i++) {
			for(int j=0; j<9; j++) {
				scanf(" %c",&ch);//注意前导空格
//scanf(" %c",&c)这个空格(换成\n或者\t也可以),把缓冲区中的回车当成第一个字符,读取后丢掉。
				mp[i][j]=ch-'0';
				if(ch=='0') {  //是空格记录其位置 
					pos[num][0]=i;
					pos[num++][1]=j;
				}
			}
		}
		dfs(num-1);   //从最后一个空格位置开始深搜 
		for(int i=0; i<9; i++) {
			for(int j=0; j<9; j++)
				printf("%d",mp[i][j]);
			printf("\n");
		}
	}
	return 0;
}

 

 

POJ 1101 Sticks(剪枝范例)

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input

The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output should contains the smallest possible length of original sticks, one per line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

 

思路:一个接一个的把木棍拼起来,最后把木棍用光。
剪枝技巧:  设所有木棍的总长度是Sum, 最终的答案(长度)是 len。 
1. 首先要明白,Sum一定要能被L整除。 
2. len>=最长的木棍的长度Max
由上述两点,我们想到,可以从Max开始递增地枚举 len,直到成功地拼出 Sum/len 支长度为 len 的木棍。
3. 将输入的长度从大到小排序,这么做是因为一支长度为K的完整木棍,总比几支短的小木棍拼成的要好。
eg:如果我要拼 2 支长为8的木棍,第一支木棍我拼成5 + 3然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二支,仔细想一想,就会发现这样做没意义,注定要失败的。我们应该留下 2+1 因为 2+1 比 3 更灵活
4. 相同长度的木棍不要搜索多次, 比如:我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索状态是 5+4+…. 
(即表示长度为 5,4,2 的三支拼在一起,…表示深层的即将搜索的部分), 进行深搜后不成功,故我没必要用另一个 4 在进行 5+4+…
5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未被使用的木棍开始,如果搜索不成功,那么以比它短的开始那么也一定不能取得全局的成功。因为每一支题目给出的木棍都要被用到。 
eg:4
5 4 4 3 2
想拼成长为 6 的木棍,那么从 5 开始,但是显然没有能与 5一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
最终 5 一定会被遗弃。在拼第 2 3 … 支木棍时,一样。 

原文链接:https://blog.csdn.net/qq_32426313/article/details/52083559

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[69];//存小棒长大小最好大于64
int visited[69];//标记小棒是否被用
int n;//小棒个数
int cmp(int a,int b) {
	return a>b;
}
int DFS(int len,int left,int num) {//假设最小棒长len,当前拼成len长的棒还需的长度left,剩下尚未拼接过的小棒个数num
	if(left==0&&num==0)//当还需长度为0且未作拼接的小棒个数为0时,说明拼接成功
		return len;
	if(left==0)//当拼完一个棒时,要重新进行下一根木棒拼接,给left重新赋值
		left=len;
	for(int i=0; i<n; i++) {
		if(visited[i]==1)//已经作为成功拼接的小棒,不再作为拼接对象
			continue;
		if(left>=a[i]) {
			visited[i]=1;//暂时标记为已经用过 
			if(DFS(len,left-a[i],num-1))//当接受这根小棒a[i],能完成所有任务,则返回成功拼接棒长
				return len;
			visited[i]=0;//当用a[i]不能完成任务就不用此棒,将其标记为该步未用此棒
			if(a[i]==left||len==left)//a[i]=left表示当前即将拼成一根棒但却没用上,故拼接长度len不可能用尽所有小棒 
									//left=len表示下一轮参数与此轮参数相同,此轮未成功,故下轮也不会成功 
				break;
			while(a[i]==a[i+1])//当a[i]不能完成任务,与它相同长度的小棒也不能完成任任务 
				i++;
		}
	}
	return 0;
}
int main() {
	while(cin>>n&&n) {
		int sum=0;//所有小棒长度
		int len,ans;
		for(int i=0; i<n; i++) {
			cin>>a[i];
			sum+=a[i];
		}
		sort(a,a+n,cmp);
		for(len=a[0]; len<=sum; len++) {
			memset(visited,0,sizeof(visited));//每次尝试都要置所有小棒为可用
			if(sum%len==0) {
				ans=DFS(len,0,n);
				if(ans)
					break;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

 

Codeforces 1097A Gennady and a Card Game 

题意:桌子上有一张牌,你手上有五张牌,能否打出一张牌,能打的条件是你手上的牌和桌子上的大小一样或者花色一样,最终目的是判断你手上的牌是否有一张和桌子上牌的其中一个字母是否一样。
思路:就是判断手上的牌是否有一张牌和桌子上的牌的字母其中一个一样。

#include<iostream>
#include<string>
using namespace std;
int main() {
	int flag=0;
	string s1,s2;
	cin>>s1;
	for(int i=0;i<5;i++){
		cin>>s2;
		if(s2[0]==s1[0]||s2[1]==s1[1])
			flag=1;
	}
	if(flag) cout<<"YES\n";
	else cout<<"NO\n";
	return 0;
}

 

 

 1097B Petr and a Combination Lock

题意:给了一个圆盘,初始0度,n个角度,可以任意顺时针或者逆时针旋转,经过n次度数,能否回到0度。

思路:最开始我找的是规律,判断度数和是不是360的倍数,或者取模360后的一半是否在其中,就能回到0,结果wa5,别人大多数都是dfs…..两边方向搜,x记录个数,当x==n就判断s是否是360的倍数,就返回真或者否。s记录当前度数和。

#include<iostream>
#include<string>
using namespace std;
int n;
int a[20];
int dfs(int cnt,int sum){
	if(cnt==n){
		if(sum%360==0) return true;
		else return false;
	} 
	return dfs(cnt+1,sum+a[cnt])||dfs(cnt+1,sum-a[cnt]);
}
int main() {
	cin>>n;
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);
	cout<<(dfs(0,0)?"YES\n":"NO\n");
	return 0;
}

 

 

Codeforces 1097C. Yuhao and a Parenthesis 

 

题意:给出n个仅包括“(” 和 ")"的字符串,你需要从这些字符串中进行两两组合,使得两个串组成一个串之后,所组成的括号序列是正确的,要求组成尽量多的正确括号序列串
分析:对于任意一个括号序列,我们都可以让它进行自身匹配之后形成一个新的序列。比如 “)())”,在进行朴素的括号匹配之后形成的是 “))”,显然我们可以得出这样的结论,对于任意一个括号序列,在进行任意的括号匹配之后,该序列会称为以下几种序列:

" " ,第一种,空序列
" (((… ",第二种,包含若干个左括号的序列
" )))… " ,第三种,包含若干个右括号的序列
" ))…((… " ,第四种,包含若干个右括号,和若干个左括号的序列

显然:
空序列和空序列组合的结果是正确的
x 个左括号和 x 个右括号组合的结果是正确的,即第二种可以和第三种进行组合(如果个数相同)
第四种不符合要求,因为它和上边的任意一种都不能形成正确的组合


原文:https://blog.csdn.net/hpuer_random/article/details/85951975 

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int Left[500005],Right[500005];
int main(){
	string s;
	int n,well=0;
	cin>>n;
	while(n--){
		cin>>s;
		int l=0,r=0;
		for(int i=0;i<s.size();i++){
			if(s[i]=='(') l++;
			else{
				if(l) l--;
				else r++;
			}
		}
		if(l&&r) continue;//右括号在左边
		else if(!l&&!r) well++; 
		else if(!l&&r) Right[r]++;
		else Left[l]++;
	}
	int ans=0;
	for(int i=1;i<=500005;i++)//遍历全体 
		ans+=min(Left[i],Right[i]);
	ans+=well/2;//本身就完美的两两配对
	cout<<ans; 
	return 0;
} 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值