类别 | 集训中 | 集训后 | 总计 |
---|---|---|---|
Codeforces | 9 | 3 | 12 |
挑战 | 20 | 5 | 25 |
多校 | 2 | 1 | 3 |
合计 | 31 | 9 | 40 |
CF篇
1)Educational Codeforces Round 15, problem: (C) Cellular Network
题意:
几个城市,几个塔,在一条直线上,已知他们的坐标,每个城市至少在R这一范围内有一座塔的信号,求R得最小值
题解:
比较坑,自己YY的二指针,比赛的时候没有搞出来,颇为尴尬。几次遇到int爆数据的问题,以后记得改啊,没事别用int啊。
思路就是求每个城市到塔的最近距离求最大值,由于是不减数列,所以从第一个城市开始,找到离它最近的塔,那么之后的城市的最近的塔只会在后面出现,于是简化了复杂度。
注意当到第一个塔和第二个塔距离相等时候直接下一步,这个时候记录的话会错,
10 10
1 1 2 2 2 4 4 6 7 9
0 1 3 3 3 6 7 8 9 10
比如这样的数据,要是相等就记录下来,哈哈哈哈,你就跑不出333这个序列啦,是不是一脸嘲笑的数据而你移动指针,这时候到第一个塔的值更新了,并不会丢失数据
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include <queue>
#include<string>
using namespace std;
int main(){
int n,m;
scanf("%d%d",&n,&m);
long long city[100005];
long long tower[100005];
memset(city,0,sizeof(city));
memset(tower,0,sizeof(tower));
for(int i = 0;i<n;i++){
scanf("%lld",&city[i]);
}
for(int i = 0;i<m;i++){
scanf("%lld",&tower[i]);
}
int step =0;
int num = 0;
int ans[100005];
for(int i = 0;i<n;i++){
for(int j =step;j<m;){
long long a = abs( city[i]-tower[j]);
long long b = 0x7f7f7f7f;
if(j==m-1){
b = 0x7f7f7f7f;
}
else{
b = abs( city[i]-tower[j+1]);
}
if(a>=b){j++;step++;}
else{
ans[num] = a;
num++;
break;
}
}
}
int anss=-1;
for(int i = 0;i<num;i++){
if(ans[i]>anss){anss=ans[i];}
}
printf("%d\n",anss);
}
2)Codeforces Round #363 (Div. 2), problem: (B)
题意:一个炸弹,可以消灭横竖所有墙,问是否能用一个boom消灭地图上所有墙
题解:好吧,这个有点技巧,做法是计算每一列的墙数,存在sl[],计算每一排墙数,存在sh[]中。遍历每一个点,当这个点对应的sl[i]和sh[j]之和减去(这个点是否是墙)==墙数时,则这个点成立。
(if(sh[i]+sl[j]-(maze[i][j]==’*’)==num))
#include<iostream>
#include<sstream>
#include<string>
#include<cstdlib>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cctype>
#include<set>
#include<bitset>
#include<algorithm>
#include<list>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<time.h>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
using namespace std;
int sh[1002];
int sl[1002];
int num = 0;
char maze[1005][1005];
int main(){
int n,m;
scanf("%d %d",&n,&m);
getchar();
memset(sh,0,sizeof(sh));
memset(sl,0,sizeof(sl));
for(int i = 1;i<=n;i++){
for(int j = 1;j<=m+1;j++){
scanf("%c",&maze[i][j]);
if(maze[i][j]=='*'){
num++;
sh[i]++;
sl[j]++;
}
}
}
for(int i = 1;i<=n;i++){
for(int j = 1;j<=m;j++){
if(sh[i]+sl[j]-(maze[i][j]=='*')==num){
cout<<"YES"<<endl;
cout<<i<<" "<<j<<endl;
return 0;
}
}
}
cout<<"NO"<<endl;
}
3)Codeforces Round #363 (Div. 2), problem: (C)
题意:一个人每天按照情况可以选择做事或者休息,但是他连续两天不做同一件事,这天能不能做某件事是受条件限制的,求休息的最短天数
题解:
dp,对于每一天的条件下的休息时间的变化是根据以前的来增长的。dp[105][3],列数用来表示每一天,行数0,1,2表示这天在0,1,2这三种情况对这一天的影响。
如果这一天是1,那么你只能选择做1或者休息,那么
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
而对于今天是0的dp[i][0]来说,没有影响,依旧是
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
不存在做2 的情况,所以dp[i][2]是一种无穷大的情况。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
#define pr(x) cout << #x << ": " << x << " "
#define pl(x) cout << #x << ": " << x << endl;
#define pri(a) printf("%d\n",(a))
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
#define sai(n) scanf("%I64d", &(n))
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++)
const int INF = 0x3f3f3f3f;
int dp[105][3];
int main(){
int n;
cin>>n;
memset(dp,0x3f,sizeof(dp));
int op;
dp[0][0] = 0;
for(int i = 1;i<=n;i++){
cin>>op;
if(op == 0){
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 1){
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 2){
dp[i][2] = min(dp[i-1][0],dp[i-1][1]);
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 3){
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
dp[i][2] = min(dp[i-1][0],dp[i-1][1]);
}
}
int ans = INF;
ans = min(dp[n][0],min(dp[n][1],dp[n][2]));
cout<<ans<<endl;
}
4)Codeforces Round #364 (Div. 2), problem: (B) Cells Not Under Attack
题意:
给你一个地图。在其中安放炮塔。炮台可以打到与它同行同列的所有地方,求每一次安放一个炮台,剩余多少地方打不到
题解:
每一次认为是减少了矩形的长和宽,对于安放的地方已有同行的炮台,则只是矩形长减一,对于安放的地方已有同列的炮台,则只是矩形宽减一。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> p;
typedef long long ll;
typedef unsigned long long ull;
#define pr(x) cout << #x << ": " << x << " "
#define pl(x) cout << #x << ": " << x << endl;
#define pri(a) printf("%d\n",(a))
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
#define sai(n) scanf("%I64d", &(n))
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++)
int main(){
int n,num;
cin>>n>>num;
bool h[n+5];
bool l[n+5];
long long step = 1;
long long nx=n; long long ny=n;
long long ans[num+5];
memset(h,false,sizeof(h));
memset(l,false,sizeof(l));
int x;int y;
while(cin>>x>>y){
if(l[y]==false){
l[y]=true;
ny--;
}
if(h[x]==false){
h[x]=true;
nx--;
}
long long a = nx*ny;
ans[step] = a;
step++;
}
for(int i=1;i<=num;i++){
cout<<ans[i]<<" ";
}
return 0;
}
5)Codeforces Round #364 (Div. 2), problem: (D) As Fast As Possible
题意:
一群人去一个地方,只有一辆车,已知人的速度,路程,车的速度,人数和每一次车的最大载客量,且每个人只能坐一次车。求最短到达时间。
题解:
人的行程分为走路和坐车,当且仅当走路的人和最后一批坐车的人同时到达时,时间最短,按照对称性,可以知道每一批人坐车和走路的时间都是一样的,所以最后是推公式得到坐车和走路的时间。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
#include <string>
#include <string.h>
using namespace std;
typedef pair<int, int> p;
typedef long long ll;
typedef unsigned long long ull;
#define pr(x) cout << #x << ": " << x << " "
#define pl(x) cout << #x << ": " << x << endl;
#define pri(a) printf("%d\n",(a))
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
#define sai(n) scanf("%I64d", &(n))
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++)
int main(){
int n,k;
double l,v1,v2;
cin>>n>>l>>v1>>v2>>k;
int step;
if(n%k==0){step = n/k;}
else{step = n/k+1;}
double a=(v1+v2)*l/(v1+v2+2*(step-1)*v1);
double ans = a/v2+(l-a)/v1;
printf("%.10f\n",ans);
}
6)Educational Codeforces Round 15, problem: (B) Powers of Two
题意:
求两个数加起来是2的次方,这样的数对在数列中有多少组
题解:
爆搜直接over,做出来都有鬼,比较坑的是自己YY的log发生了莫名的错误,卡在第一组很久,颇为尴尬,正解是map,键值是数值,实值是该键值在数列中对应的个数,遍历直到2的32次方然后记录即可
for(int i =1;i<32;i++){
ans+= mp[(1<<i)-x];
}
mp[x]++;
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include <queue>
#include<string>
#include<cmath>
using namespace std;
map <int ,int > mp;
int main(){
int n;
scanf("%d",&n);
int num[100005];
long long ans = 0;
for(int i = 0;i<n;i++){
int x;scanf("%d",&x);
for(int i =1;i<32;i++){
ans+= mp[(1<<i)-x];
}
mp[x]++;
}
printf("%I64d",ans);
}
7)CF Educational Round 12, B
题意:
给你一组数字(从1到n,乱序),每次让你找到某一个数字,记录下它的位置i,然后将i加到ans上,然后将这个数字提到队首,于是在新的数字排序中找下一个数,最后输出ans
题解:
模拟整个过程,暴力解法,注意每次初始化小数组储存用户的order
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main(){
int str[103];
int numofcus;
int numoforder;
int numofitem;
int ans=0;
cin>>numofcus>>numoforder>>numofitem;
for(int i=1;i<=numofitem;i++){cin>>str[i];}
int turn=numofcus;
while(turn){
int cusorder[numoforder];
memset(cusorder,0,sizeof(cusorder));
for(int i=0;i<numoforder;i++){
cin>>cusorder[i];
}
for(int i=0;i<numoforder;i++){
for(int j=1;j<=numofitem;j++){
if(cusorder[i]==str[j]){
ans+=j;
int temp=str[j];
for(int k=j;k>1;k--){//重组数组
str[k]=str[k-1];
}
str[1]=temp;
j=1;
break;
}
}
}
turn--;
}
cout<<ans<<endl;
}
8)Codeforces Round #363 (Div. 2), problem: (C)
题意:一个人每天按照情况可以选择做事或者休息,但是他连续两天不做同一件事,这天能不能做某件事是受条件限制的,求休息的最短天数
题解:
dp,对于每一天的条件下的休息时间的变化是根据以前的来增长的。dp[105][3],列数用来表示每一天,行数0,1,2表示这天在0,1,2这三种情况对这一天的影响。
如果这一天是1,那么你只能选择做1或者休息,那么
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
而对于今天是0的dp[i][0]来说,没有影响,依旧是
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
不存在做2 的情况,所以dp[i][2]是一种无穷大的情况。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
#define pr(x) cout << #x << ": " << x << " "
#define pl(x) cout << #x << ": " << x << endl;
#define pri(a) printf("%d\n",(a))
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
#define sai(n) scanf("%I64d", &(n))
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++)
const int INF = 0x3f3f3f3f;
int dp[105][3];
int main(){
int n;
cin>>n;
memset(dp,0x3f,sizeof(dp));
int op;
dp[0][0] = 0;
for(int i = 1;i<=n;i++){
cin>>op;
if(op == 0){
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 1){
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 2){
dp[i][2] = min(dp[i-1][0],dp[i-1][1]);
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
}
if(op == 3){
dp[i][0] = 1 + min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]));
dp[i][1] = min(dp[i-1][0],dp[i-1][2]);
dp[i][2] = min(dp[i-1][0],dp[i-1][1]);
}
}
int ans = INF;
ans = min(dp[n][0],min(dp[n][1],dp[n][2]));
cout<<ans<<endl;
}
9)Codeforces Round #201 (Div. 2)C——数论
题意:
A 和 B玩游戏,A每次先手,游戏规则:在一个集合中找到两个数,他们的差绝对值不在集合中,则添加到集合里。如果无法选择了,那个人就是输了。
题解:
模拟不可能,这题找规律。
好吧,证明下面的结论也是无力。
所有初始数的最大公约数gcd,集合={1*gcd,2*gcd,…k*gcd≤max}max=所有初始数的最大值。
int gcd(int a, int b) {
if(b == 0)
return a;
return gcd(b, a % b);
}
这里贴一下gcd代码,自己写的就是垃圾。。。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
int gcd(int x,int y){
if(x<y)
swap(x,y);
if(x%y==0){return y;}
else {
int temp=x;
x=y;
y=temp%y;
return gcd(x,y);
}
}
int main(){
int n;
cin>>n;
int num[105];
int maxn=-1;
for(int i=1;i<=n;i++){
cin>>num[i];
if(num[i]>maxn){maxn=num[i];}
}
int temp= gcd(num[1],num[2]);
for(int i=3;i<=n;i++){
temp=gcd(temp,num[i]);
}
if((maxn/temp-n)%2==0)cout<<"Bob"<<endl;
else cout<<"Alice"<<endl;
}
总结:第一次接触数论的题,这种“guess+几种样例观察结果”的方法,或许是一种可行的解法。。。
10)CF Educational Round 12, C
题意:
给一个字符串,要求通过最少的改变(每次只能改变一个字符)将其变成两两之间互不相等的字符串(有多种结果,只需要输出其中一种)
题解:
我的思路:首先两两比较,若相等,则将后面一个变换。使得变换后的字符不等于他的下一个字符(即aab->acb,而不是aab->abb)[好吧,自己在写的时候坑了自己,就是我是使用直接加减来改变,坑自己的点在y上(如果y后面是z,那么我的代码就使得y+了2.。。。于是就超出了范围。。。泪崩。。好吧,我还是觉得使用round比较好,,判断一下重来就行了)]
#include<iostream>
#include<string>
#include<stdio.h>
using namespace std;
int main(){
string s;
cin>>s;
for(int i=1;i<s.size();){
if(i==s.size()-1)
{
if(s[i-1]==s[i])
{
if(s[i]=='z'){s[i]-=1;}
else if(s[i]=='a'){s[i]+=1;}
else{s[i]+=1;}
}
break;
}
if(s[i]==s[i-1]){
if(s[i]=='z')
{
s[i]-=1;
if(s[i+1]==s[i])
{s[i]-=1;}
}
else if(s[i]=='a')
{
s[i]+=1;
if(s[i+1]==s[i])
{s[i]+=1;}
}
else
{
s[i]+=1;
if(s[i+1]==s[i])
{s[i]+=1;if(s[i]>'z')s[i]-=3;}
}
i+=2;continue;
}
i++;
}
cout<<s<<endl;
return 0;
}
11)Codeforces Round #348,D
题意:
给你一组数1~n,有三种转换方式。一,左旋转。二,右旋转。三,位置1和位置2互换;位置3和位置4互换,以此类推。最后输出这组数据。
题解:
我的思路:1.q1[],q2[]储存变换方式的数据,2.旋转方式使用函数change(这是一个旋转的写法:首先,旋转可以看做是两段数据之间的变换,具体方式:两段数据分别做反转,再将整体做反转。例如:1234->12 34->21 43->3412,这样就实现了左旋转2位)3.互换,使用swap解决。
PS:最后这个方法失败了。当测试很大的数据的时候就time limit。。。估计是旋转方式需要对每一个数数据处理,太复杂。
AC方法:实际上,我们只需要确定1,2的位置就能找到整组数的排列方式,所以,所有的工作就是对于初始位于位置1,2的数字1,2的变换。
通过判断是否出现倒回(即从头一步之后到尾;从尾一步之后到头)分成两种情况
在有倒回的这种情况中,根据我的方法实际有效的移动的步数是q2[i]%n,这就出现了如果刚好回到原始点(即q2[i]%n==0)便不再实用我推的公式(这个就是自己被坑,调了好久才知道的地方),所以此情况再分成2种。
/*#include<iostream>
#include<cstdio>
using namespace std;
void change(int *arr,int head,int bottem){
for(;head<bottem;head++,bottem--){
int temp;
temp=arr[head];
arr[head]=arr[bottem];
arr[bottem]=temp;
}
}
int main(){
int n;
int q;
cin>>n>>q;
int num[n+5];
int q1[q+5];
int q2[q+5];
for(int i=1;i<=q;i++){
scanf("%d",&q1[i]);
if(q1[i]==1){scanf("%d",&q2[i]);}
}
for(int i=1;i<=n;i++){
num[i]=i;
}
for(int i=1;i<=q;i++){
if(q1[i]==1){
if(q2[i]>=0){
//向右移动
change(num,1,n-q2[i]);
change(num,n-q2[i]+1,n);
change(num,1,n);
}
if(q2[i]<0){
//向左移动
change(num,1,-q2[i]);
change(num,-q2[i]+1,n);
change(num,1,n);
}
}
else {//交换
for(int i=1;i<=n;){
swap(num[i],num[i+1]);
i+=2;
}
}
}
for(int i=1;i<=n;i++){
printf("%d ",num[i]);
}
}
*/
#include<iostream>
#include<cstdio>
using namespace std;
int main(){
int n;
int q;
cin>>n>>q;
int num[n+5];
int q1[q+5];
int q2[q+5];
for(int i=1;i<=q;i++){
scanf("%d",&q1[i]);
if(q1[i]==1){scanf("%d",&q2[i]);}
}
int index1=1;
int index2=2;
for(int i=1;i<=q;i++){
if(q1[i]==1){
if(q2[i]>=0){
if(index1+q2[i]<=n){index1+=q2[i];}
else {
if(q2[i]%n==0){;}
else{index1=index1+(q2[i]%n)-n;}
}
if(index2+q2[i]<=n){index2+=q2[i];}
else {
if(q2[i]%n==0){;}
else {index2=index2+(q2[i]%n)-n;}
}
}
if(q2[i]<0){
if(index2+q2[i]>=1){index2=q2[i]+index2;}
else {
if(-q2[i]%n==0){;}
else {index2=index2+n+(q2[i]%n);}
}
if(index1+q2[i]>=1){index1=q2[i]+index1;}
else {
if(-q2[i]%n==0){;}
else {index1=index1+n+(q2[i]%n);}
}
}
}
else{
if(index1%2==0){index1-=1;}
else{index1+=1;}
if(index2%2==0){index2-=1;}
else{index2+=1;}
}
}
num[index1]=1;
num[index2]=2;
for(int i=index1+2,j=index2+2,k=1;k<n/2;k++){
if(i>n)i-=n;
if(j>n)j-=n;
num[i]=2*k+1;
num[j]=2*k+2;
i+=2;
j+=2;
}
for(int i=1;i<=n;i++){
printf("%d ",num[i]);
}
}
12)Codeforces Round #348,C
题意:
有一个矩阵(N*M),经过若干种变化,行旋转,列旋转,赋值。得到最终的矩阵。求初始矩阵是多少。
题解:
1.用数组来储存每一组数据(这里使用scanf来输入,特别是数据每种情况下数量不同,需要判断)
2.从最后一组数据开始,暴力向上一步一步模拟(好吧,这里吐槽一下我自己在写代码的时候使用了0行0列,写的时候巨烦。。。。)
3.所谓的旋转,例如行左旋转,就是temp=末尾,然后从倒数第二个开始,前一次向后赋值,最后首个数据=temp,这是最简单的方法
4. 注意输出时:%d后的空格,以及一行输出后的换行。(注意格式)
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
printf("%d ",matrix[i][j]);
}
cout<<endl;
}
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int main(){
int n;
int m;
int op;
cin>>n>>m>>op;
int matrix[n][m];
memset(matrix,0,sizeof(matrix));
int t[10100],r[10100],c[10100],x[10100];
for(int i=0;i<op;i++){
scanf("%d",&t[i]);
if(t[i]==1) scanf("%d",&r[i]);
else if(t[i]==2) scanf("%d",&c[i]);
else scanf("%d%d%d",&r[i],&c[i],&x[i]);
}
for(int i=op-1;i>=0;i--){
if(t[i]==3)
{
matrix[r[i]-1][c[i]-1]=x[i];
}
else if(t[i]==2) {
int temp;
temp=matrix[n-1][c[i]-1];
for(int j=n-1;j>0;j--){
matrix[j][c[i]-1]=matrix[j-1][c[i]-1];
}
matrix[0][c[i]-1]=temp;
}//changecolumn(matrix,c[i]);
else {
int temp;
temp=matrix[r[i]-1][m-1];
for(int j=m-1;j>0;j--){
matrix[r[i]-1][j]=matrix[r[i]-1][j-1];
}
matrix[r[i]-1][0]=temp;
}//changerow(matrix,r[i]);
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
printf("%d ",matrix[i][j]);
}
cout<<endl;
}
}
挑战篇
1.DFS
1)Poj 1979
题意:一个地图,求从起点开始,可以到达的点的个数,其中有墙阻隔,默认有边界。
题解:DFS,从起点开始,先替换这个点使之成为墙,然后四个方向进行搜索,知道没有点为止。
注意:在输入地图时,应该注意加一个为止存放’\n’,所以第二层for要+1宽度;还有在输入地图规格时候注意加上getchar,以免后面混乱
(当输入的是数字地图时候,不需要加宽)
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
using namespace std;
char maze[30][30];
int ans=0;
int dx[] = {0,0,-1,1};
int dy[] = {-1,1,0,0};
int H,L;
void dfs(int x,int y){
maze[x][y]='#';
ans++;
for(int i = 0; i<4; i++){
int nx = x + dx[i];
int ny = y + dy[i];
// pl(maze[nx][ny]);
if(nx<L&&nx>=0&&ny<H&&ny>=0&&maze[nx][ny]=='.')
{
dfs(nx,ny);
}
}
}
int main(){
while(1){
int x,y;
scanf("%d %d",&L,&H);
getchar();
if(H==0){return 0;}
for(int i = 0; i<H; i++){
for(int j = 0; j<L+1; j++){
scanf("%c",&maze[j][i]);
if(maze[j][i]=='@'){
x=j;
y=i;
}
}
}
dfs(x,y);
printf("%d\n",ans);
ans=0;
}
}
2)Poj 3009
题意:
地图上有起点和终点,你每次向四个方向移动冰球,知道它撞到障碍位置,且此时此障碍消失。注意:你必须有扔球的缓冲区域,即四个方向有一个是没有障碍物的,求是否可以到终点一个到终点的最小步数,大于10步就算不能到达,球在运动过程中不能出界。
题解:
1.dfs包含3个参数 x,y,step,每一次for里面只会出现遇到1,遇到3,出界的情况,用是否出界来作为每一次dfs的结束条件while (judge(a, b))
2.if (step > 10) return
;
用来减枝
3.由于有缓冲区间,cflag用来判断是否是有缓冲区间,然后返回到上一个点,进行下一次dfs
if (grid[a][b] == 1 && cflag > 1) {
grid[a][b] = 0;
dfs(a-dir[i][0], b-dir[i][1], step+1);
grid[a][b] = 1;
}
其中需要注意的是 grid[a][b] = 1; 这是还原地图,因为你每次完成一次初始第一步之后的所有dfs之后,需要还原这个地图,这句非常重要
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
const int INFS = 0x3fffffff;
int grid[25][25], row, col, ans;
int dir[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1};
inline bool judge(int x, int y) {
if (0 < x && x <= row && 0 < y && y <= col) {
return true;
}
return false;
}
void dfs(int x, int y, int step) {
if (step > 10)
return ;
for (int i = 0; i < 4; ++i) {
int a = x, b = y, cflag = 0;
while (judge(a, b)) {
a += dir[i][0];
b += dir[i][1];
cflag += 1;
if (grid[a][b] == 3) {
ans = min(ans, step + 1);
return ;
}
if (grid[a][b] == 1)
break ;
}
if (grid[a][b] == 1 && cflag > 1) {
grid[a][b] = 0;
dfs(a-dir[i][0], b-dir[i][1], step+1);
grid[a][b] = 1;
}
}
}
int main() {
while (scanf("%d%d", &col, &row) && col && row) {
int x, y;
memset(grid, 0, sizeof(grid));
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= col; j++) {
scanf("%d", &grid[i][j]);
if (grid[i][j] == 2)
x = i, y = j;
}
}
ans = INFS;
dfs(x, y, 0);
if (ans <= 10)
printf("%d\n", ans);
else
printf("-1\n");
}
}
3)AOJ 0033
题意:有一个数列,1-10,10个元素乱排,问是否可以将数列按顺序抽出元素组成新数列,使得新数列是递增的而且原数列剩下的数字也是递增的
题解:
dfs,记录步数和当前两个新数列的末尾,如果可以放,就下一轮dfs,完全是模拟了题目的一个图形
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
int num[10];
bool dfs(int step,int btop,int ctop){
if(step==9){
if(num[step]>btop||num[step]>ctop){
return true;
}
else return false;
}
bool b = false;
bool c = false;
if(num[step]>btop){b = dfs(step+1,num[step],ctop);}
if(num[step]>ctop){c = dfs(step+1,btop,num[step]);}
return (b||c);
}
int main(){
int t;
cin>>t;
while(t--){
memset(num,0,sizeof(num));
for(int i = 0;i<10;i++){
cin>>num[i];
}
bool ans = dfs(0,0,0);
if(ans){cout<<"YES"<<endl;continue;}
else{cout<<"NO"<<endl;continue;}
}
}
2.BFS
1)Poj 3669
题意:
有个人去看流星雨,不料流星掉下来会砸毁上下左右中五个点。每个流星掉下的位置和时间都不同,人不能待在流星毁过的地方,人从0,0出发,每次人只能走上下左右中一步,求他是否能求小文青能否到安全的地方,如果能,最短的逃跑时间是多少?
题解:
1.对地图初始化,流星破坏的地方设置成他的下落时间,其他地方设置成INF
2.struct里面记录坐标和时间,每一次的上下左右移动需要判断出界和时间加+1,并且要记录是否来过,从而剪枝,如果走到了地方是INF,那么可以,否则输出-1。
注意:
1.这个题是地图初始化,从而得到成立条件。
2.visited[][]记录每个点是否到达,剪枝
3.结构体里面的参数设置
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
using namespace std;
int maze[305][305];
int dx[] = {0,0,-1,1};
int dy[] = {-1,1,0,0};
bool visited[305][305];
const int INF = 0x3f3f3f3f;
struct p{
int x,y,time;
};
int bfs(){
queue<p> que;
p np;
np.x=0;np.y=0;np.time=0;
que.push(np);
visited[0][0] = true;
if(maze[0][0]==INF){return 0;}
if(maze[0][0]==0){return -1;}
while(que.size()){
p cp = que.front();
que.pop();
for(int i = 0;i < 4;i++){
np = cp;
np.x = np.x + dx[i];
np.y = np.y + dy[i];
np.time = np.time+1;
if(np.time<maze[np.x][np.y]&&!visited[np.x][np.y]&&np.x>=0&&np.y>=0){
if(maze[np.x][np.y]==INF){return np.time;}
que.push(np);
visited[np.x][np.y] = true;
}
}
}
return -1;
}
int main(){
int n;
scanf("%d",&n);
int a,b,t;
for(int i = 0;i<305;i++){
for(int j = 0;j<305;j++){
maze[i][j] = INF;
visited[i][j] = false;
}
}
while(n--){
scanf("%d %d %d",&a,&b,&t);
maze[a][b] = min(maze[a][b],t);
for(int j = 0; j<4 ;j++){
if((a+dx[j])>=0&&(b+dy[j])>=0)
{
maze[a+dx[j]][b+dy[j]] = min(maze[a+dx[j]][b+dy[j]],t);
}
}
}
int ans = bfs();
printf("%d\n",ans);
}
2)AOJ 0558
题意:
一个地图,需要先从起点到1再到2,,,,以此类推,问最短路程
题解:
明显最短路程就是bfs,每一次把起点和目的地换了,多次bfs
注意:输入地图时,可以使用如下方法,避免每一个字符输入时对‘\n’还要处理
cin>>h>>w>>n;
getchar();
for(int i = 0;i<h;i++){
scanf("%s",&maze[i]);
}
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
char maze[1005][1005];
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
int d[1005][1005];
const int INF = 0x7f7f7f7f;
typedef pair <int ,int > p;
queue <p> que;
int h,w;
void chushihua(){
for(int i = 0;i<1005;i++){
for(int j = 0;j<1005;j++){
d[i][j] = INF;
}
}
}
int bfs(int x,int y,int gx,int gy){
chushihua();
d[x][y] = 0;
que.push(p(x,y));
while(que.size()){
p cp = que.front();
que.pop();
if(cp.first==gx&&cp.second==gy){break;}
for(int i = 0;i<4;i++){
int nx = cp.first + dx[i];
int ny = cp.second + dy[i];
if(nx>=0&&nx<h&&ny>=0&&ny<w&&maze[nx][ny]!='X'&&d[nx][ny]==INF){
que.push(p(nx,ny));
d[nx][ny] = d[cp.first][cp.second] + 1;
}
}
}
while(que.size()){que.pop();}
return d[gx][gy];
}
int main(){
int n;
cin>>h>>w>>n;
int indexmap[n+5][4];
getchar();
for(int i = 0;i<h;i++){
scanf("%s",&maze[i]);
}
for(int i = 0;i<h;i++){
for(int j = 0;j<w;j++){
if(maze[i][j]=='S'){
indexmap[0][0] = i;
indexmap[0][1] = j;
}
else if((int)maze[i][j]>=49&&(int)maze[i][j]<=57){
int index = maze[i][j]-'0';
indexmap[index][0] = i;
indexmap[index][1] = j;
}
}
}
int ans = 0;
for(int i=0;i<n;i++){
ans += bfs(indexmap[i][0],indexmap[i][1],indexmap[i+1][0],indexmap[i+1][1]);
}
cout<<ans<<endl;
}
3)AOJ 0121
题意:
对于一个4X2的方格,有7个方片对应1–7,一个空格对应0,移动使得方格从左到右从上到下依次是01234567,问最短步数
题解:
使用map,map有两个数,以第一个数作为排序的标准,以第二个数作为该map对应的值
map <string, int> dp;
对于每一次的移动,直接抽象到一行来,注意条件的判断
int direction[4] = { 1, -1, 4, -4 };
int n = index + direction[i];
//index是0的位置,括号分别表示右上角不能右移了和左下角不能左移了
if(n>=0&&n<8&&!(index==3&&i==0)&&!(index==4&&i==1))
注意:对于一行有空格且不知道长度的字符串,去空格的方法
string line;
while (getline(cin, line))
{
line.erase(remove(line.begin(), line.end(), ' '), line.end());
}
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include<unordered_set>
using namespace std;
map <string, int> dp;
int direction[4] = { 1, -1, 4, -4 };
void bfs(){
queue <string >que;
que.push("01234567");
dp["01234567"] = 0;
while(que.size()){
string now = que.front();
que.pop();
int index = 0;
for(int i = 0;i<8;i++){
if(now[i] =='0'){
index = i;
break;
}
}
for(int i =0;i<4;i++){
int n = index + direction[i];
if(n>=0&&n<8&&!(index==3&&i==0)&&!(index==4&&i==1)){
string next = now;
swap(next[n],next[index]);
if(dp.find(next)==dp.end()){
dp[next] = dp[now]+1;
que.push(next);
}
}
}
}
}
int main(){
bfs();
string line;
while (getline(cin, line))
{
line.erase(remove(line.begin(), line.end(), ' '), line.end());
cout << dp[line] << endl;
}
return 0;
}
3.爆搜
1)AOJ 0525
题意:
一个地图,只有0,1,每次可以转换一行或者一列,使得一行或者一列的0和1值互换,求最多可以有多少个1
题解:
先对行爆搜,以为题中行数少,然后对每一列记录1的个数和0的个数,取较大值,其中使用了bitset简单的实现了转换
bitset <10000> a[10];
对于行的爆搜,使用了二进制转换从0到pow(2,r)-1,每个数的二进制数对应是否转换这一行
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include<unordered_set>
#include<bitset>
using namespace std;
bitset <10000> a[10];
int str[10];
void change(int x){
memset(str,0,sizeof(str));
for(int i = 0;x>0;i++){
str[i] = x%2;//pl(str[i]);
x = x/2;
}
}
int main(){
int r,c;
while(cin>>r>>c&&r!=0){
int ans =0;
for(int i = 0;i<r;i++){
for(int j = 0;j<c;j++){
bool b;
cin>>b;
a[i][j] = b;
}
}
for(int i = 0;i<pow(2,r);i++){
int anss=0;
change(i);
for(int j = 0;j<r;j++){
if(str[j]==1){a[j].flip();}
}
for(int k = 0;k<c;k++){
int ccount =0;
for(int i =0;i<r;i++){
if(a[i][k]==1){ccount++;}
}
//pl(ccount);
anss +=max(ccount,r-ccount);
}
ans = max(ans,anss);
for(int j = 0;j<r;j++){
if(str[j]==1){a[j].flip();}
}
}
cout<<ans<<endl;
}
}
2)POJ 2718
题意:
对于一个数列,我们将数字分成两堆,求可能的情况下,两堆数字组成的数的差的最小值,排除前导0
题解:
将给出的数列进行字典序排列,然后将每个部分的转化成数字做差比较,因为字典序,所以枚举了所有数字
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include<bitset>
#include<string>
using namespace std;
int calnum(int str[],int eindex){
int a= 0;
for(int i = 0 ;i<eindex;i++){
a = str[i]+a*10;
}
return a;
}
int main(){
int t;
cin>>t;
cin.ignore();
while(t--){
string s;
getline(cin,s);
s.erase(remove(s.begin(),s.end(),' '),s.end());
int length = s.size();
int nums[length];
for(int i = 0;i<length;i++){
nums[i] = s[i]-'0';
}
int numa[length];
int numb[length];
int bendindex;
int ans=0x7f7f7f7f;
if(length%2==0){
bendindex = length/2;
}
else{
bendindex = length/2+1;
}
do{
for(int i = 0;i<length/2;i++){
numa[i] = nums[i];
}
for(int i = length/2,j = 0;i<length;i++,j++){
numb[j] = nums[i];
}
if((numa[0]==0&&length/2>1)||
(numb[0]==0&&bendindex>1)
){continue;}
else{
int a = calnum(numa,length/2);
int b = calnum(numb,bendindex);
ans = min(ans,abs(a-b));
}
}while(next_permutation(nums,nums+length));
cout<<ans<<endl;
}
}
3)POJ 3050
题意:从一个起点,走5步,能组成几个六位数
题解:
用set直接排除重复项最easy,爆搜每一个起点
dfs(int x,int y,int step,int s)
if(step==5){
if(ansset.find(s)==ansset.end()){
ansset.insert(s);
}
return;
}
实际上并不需要判断,因为set不会装重复元素
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include<bitset>
#include<string>
#include<cstring>
#include<sstream>
using namespace std;
int maze[5][5];
set<int> ansset;
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
bool isin(int x,int y){
if(x>=0&&x<5&&y>=0&&y<5){return true;}
else{return false;}
}
void dfs(int x,int y,int step,int s){
if(step==5){
if(ansset.find(s)==ansset.end()){
ansset.insert(s);
}
return;
}
for(int i =0;i<4;i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(isin(nx,ny)){
dfs(nx,ny,step+1,s*10+ maze[nx][ny]);
}
}
}
int main(){
for(int i = 0;i<5;i++){
for(int j = 0;j<5;j++){
cin>>maze[i][j];
}
}
for(int i = 0;i<5;i++){
for(int j = 0;j<5;j++){
dfs(i,j,0,maze[i][j]);
}
}
cout<<ansset.size()<<endl;
}
4.贪心
1)POJ 2376
题目:
给定一个时间T和N个时间区间,求最少需要多少个区间覆盖总区间[1,T],无法覆盖区域[1,T]时输出-1。
例如T=10,有3个区间[1,7],[3,6],[6,10],则最少需要两个区间来覆盖,选择区间1和区间3。
题解:
先给区域排序,起始点为1 的开始,遍历得到起始点为1,终点最远的位置,然后判断他是否有更新,没有就输出-1,直到end>=t输出。
bool cmp(cow a,cow b){
return a.begin<b.begin||(a.begin==b.begin&&a.end<b.end);
}
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#include<set>
#include<map>
#include<bitset>
#include<string>
#include<cstring>
#include<sstream>
using namespace std;
struct cow{
int begin;
int end;
};
cow cowstr[25000];
bool cmp(cow a,cow b){
return a.begin<b.begin||(a.begin==b.begin&&a.end<b.end);
}
int main(){
int n,t;
cin>>n>>t;
for(int i = 0;i<n;i++){
cin>>cowstr[i].begin>>cowstr[i].end;
}
sort(cowstr,cowstr+n,cmp);
int ans = 0;
int end = 0;
int startindex = 0;
while(end<t){
int begin = end +1;
for(int i =startindex;i<n;i++){
if(cowstr[i].begin<=begin){
if(cowstr[i].end>=begin){
end = max(end,cowstr[i].end);
}
}
else{
startindex = i;
break;
}
}
if(begin>end){cout<<-1<<endl;return 0;}
else{
ans++;
}
}
cout<<ans<<endl;
}
2)POJ 1328
题意:假设海岸线是一条无限延伸的直线。每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。
题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。
一目了然,求投影到X轴上,然后重叠位置只记一次,有两种情况:当此时的线段左端点大于此时比较的右端点,不会有重合了,更新;当当此时的线段右端点小于此时比较的右端点,范围更小了,更新比较线段(这样做都是建立在sort之后,每一线段的左端点不减)
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
using namespace std;
struct point
{
double left, right;
}p[2010], temp;
bool operator < (point a, point b)
{
return a.left < b.left;
}
int main()
{
int n;
double r;
int kase = 0;
while (cin >> n >> r && (n || r))
{
bool flag = false;
for (int i = 0; i < n; i++)
{
double a, b;
cin >> a >> b;
if (fabs(b) > r)
{
flag = true;
}
else
{
p[i].left = a * 1.0 - sqrt(r * r - b * b);
p[i].right = a * 1.0 + sqrt(r * r - b * b);
}
}
cout << "Case " << ++kase << ": ";
if (flag)
{
cout << -1 << endl;
}
else
{
int countt = 1;
sort(p, p + n);
temp = p[0];
for (int i = 1; i < n; i++)
{
if (p[i].left > temp.right)
{
countt++;
temp = p[i];
}
else if (p[i].right < temp.right)
{
temp = p[i];
}
}
cout << countt << endl;
}
}
}
3)POJ 3190
题意:
每一只奶牛要求在时间区间[A,B]内独享一个牛栏。问满足奶牛整个过程最少需要多少个牛栏。
题解:
在优先队列里面设为end最前的先输出,sort以begin的时间从小到大排序。
t数组是记录每一只牛的牛栏编号
如果
c.end < cstr[i].begin
则这个栏的牛的end时间要更新了。
否则加牛栏
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
using namespace std;
struct cow{
int begin,end,num;
friend bool operator <(cow a ,cow b){
return a.end>b.end;
}
} cstr[50005];
bool cmp(cow c1,cow c2){
return c1.begin<c2.begin;
}
int t[50005];
int main(){
int n;
cin>>n;
for(int i = 0;i<n;i++){
cin>>cstr[i].begin>>cstr[i].end;
cstr[i].num = i;
}
int r= 0;
sort(cstr,cstr+n,cmp);
priority_queue <cow> q;
q.push(cstr[0]);
t[cstr[0].num] = ++r;
for(int i =1;i<n;i++){
cow c = q.top();
if(c.end < cstr[i].begin){
t[cstr[i].num] = t[c.num];
q.pop();
q.push(cstr[i]);
}
else{
t[cstr[i].num] = ++r;
q.push(cstr[i]);
}
}
cout<<r<<endl;
for(int i = 0;i<n;i++){
cout<<t[i]<<endl;
}
}
4)POJ 2393
题意:
你每周可以生产牛奶,每周生产的价格为Ci,每周需要上交的牛奶量Yi,你可以选择本周生产牛奶,也可选择提前几周生产出存储在仓库中(仓库无限大,而且保质期不考虑),每一周存仓库牛奶需要花费S元,让你求出所有周的需求量上交的最少花费。
题解:
决定你是否提前生产就是看单价,实际上对于每一次的比较只需要比较当前值和以前的最优解,如果比最优解小,那么此时他变成当前最优解。以此类推
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
using namespace std;
typedef long long ll;
int main(){
int n,s;
cin>>n>>s;
ll cost[10000];
ll need[10000];
ll ans = 0;
for(int i = 0;i<n;i++){
cin>>cost[i]>>need[i];
}
int best = 0;
for(int i =1;i<n;i++){
if(cost[i]>=(i-best)*s+cost[best]){
cost[i] = (i-best)*s+cost[best];
}
else{
best = i;
}
}
for(int i = 0 ;i<n;i++){
ans += need[i]*cost[i];
}
cout<<ans<<endl;
}
5)POJ 1017
题意:
一个工厂制造的产品形状都是长方体盒子,它们的高度都是 h,长和宽都相等,一共有六个型号,分别为1*1, 2*2, 3*3, 4*4, 5*5, 6*6。
这些产品通常使用一个 6*6*h 的长方体箱子包装然后邮寄给客户。求至少要多少个箱子。
题解:
各种讨论,遍历所有可能性
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
using namespace std;
int main(){
int b1,b2,b3,b4,b5,b6;
while(cin>>b1>>b2>>b3>>b4>>b5>>b6&&(b1+b2+b3+b4+b5+b6)){
int ans = 0;
ans += b6;
ans += b5;
b1 -= b5*11;
if(b1<0){b1 = 0;}
ans += b4;
int needs = b4*20;
while(needs>0){
if(b2>0){
needs -= 4;
b2--;
continue;
}
if(b1>0){
needs -= 1;
b1--;
continue;
}
break;
}
ans += b3/4;
if(b3%4 != 0){
ans++;
int b3num = b3%4;
int uesb2,uesb1;
uesb1=uesb2=0;
if(b3num==1){
needs = 27;
while(needs>0){
if(b2>0&&uesb2<5){
needs -= 4;
b2--;
uesb2++;
continue;
}
if(b1>0&&uesb1<7){
needs -= 1;
b1--;
uesb1++;
continue;
}
break;
}
}
if(b3num==2){
needs = 18;
while(needs>0){
if(b2>0&&uesb2<3){
needs -= 4;
b2--;
uesb2++;
continue;
}
if(b1>0&&uesb1<6){
needs -= 1;
b1--;
uesb1++;
continue;
}
break;
}
}
if(b3num==3){
needs = 9;
while(needs>0){
if(b2>0&&uesb2<1){
needs -= 4;
b2--;
uesb2++;
continue;
}
if(b1>0&&uesb1<5){
needs -= 1;
b1--;
uesb1++;
continue;
}
break;
}
}
}
ans += b2/9;
if(b2%9!=0){
ans++;
needs = 36 - b2%9*4;
b1 -= needs;
if(b1<0){b1 = 0;}
}
ans += b1/36;
if(b1%36!=0){
ans++;
}
cout<<ans<<endl;
}
}
6)POJ 3040
题意:
你有N种硬币,每种若干个,硬币的面值是一种倍数关系,你每次取不少于c面值的硬币,问可以最多去几次
题解:
正是由于面值是一种倍数关系,贪心策略就是从大到小能取多少取多少但不能超过c,然后从小到大,能取多少取多少,直到刚好超过或等于c
用need数组来存在当前情况下的最优解,然后再重复找最优解,直到找不到。
if(sum>0){
break;
}
结束条件
min(cstr[i].num - need[i],(sum + cstr[i].v-1)/cstr[i].v);
对于从小到大取,每次只允许刚刚好超过c或者等于c
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <stdio.h>
#include <cstdio>
#include <string.h>
using namespace std;
struct coin{
int v;
int num;
}cstr[30];
bool cmp(coin c1,coin c2){
return c1.v>c2.v;
}
int main(){
int n,c;
cin>>n>>c;
for(int i = 0;i<n;i++){
cin>>cstr[i].v>>cstr[i].num;
}
int ans = 0;
sort(cstr,cstr+n,cmp);
for(int i =0 ;i<n;i++){
if(cstr[i].v>=c){
ans += cstr[i].num;
cstr[i].num = 0;
}
if(cstr[i].v<c){break;}
}
int need[25];
memset(need,0,sizeof(need));
while(1){
int sum = c;
memset(need,0,sizeof(need));
for(int i = 0;i<n;i++){
if(cstr[i].num>0&&sum > 0){
int use = min(cstr[i].num,sum/cstr[i].v);
if(use >0){
sum -= use * cstr[i].v;
need[i] = use;
}
}
}
for(int i = n-1;i>=0;i--){
if(sum>0&&cstr[i].num>0){
int use = min(cstr[i].num - need[i],(sum + cstr[i].v-1)/cstr[i].v);
if(use > 0){
sum -= use * cstr[i].v;
need[i] += use;
}
}
}
if(sum>0){
break;
}
int add = 0x7f7f7f7f;
for(int i =0;i<n;i++){
if(need[i]==0){
continue;
}
add = min(add,cstr[i].num/need[i]);
}
ans += add;
for(int i = 0;i<n;i++){
cstr[i].num -= need[i] *add;
}
}
cout<<ans<<endl;
}
7)POJ 1862
题目大意:科学家发现一种奇怪的玩意,他们有重量weight,如果他们碰在一起,总重变成2*sqrt(m1*m2)。要求出最终的重量的最小值。
题解:
按照二元二次方程,当俩书越接近,所求的值越小
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <stdio.h>
#include <cstdio>
#include <string.h>
using namespace std;
bool cmp(double a,double b){
return a>b;
}
int main(){
int n;
cin>>n;
double num[105];
for(int i =0;i < n;i++){
cin>>num[i];
}
sort(num,num+n,cmp);
for(int i = 0;i<n-1;i++){
double newnum = 0;
newnum = 2*sqrt(num[i]*num[i+1]);
num[i+1] = newnum;
sort(num+i+1,num+n,cmp);
}
printf("%.3f\n",num[n-1]);
}
8)POJ 3262
题意:
有n个牛在FJ的花园乱吃。
所以FJ要赶他们回牛棚。
每个牛在被赶走之前每秒吃Di个花朵。赶它回去FJ要花的时间是Ti。在被赶走的过程中牛就不能乱吃了
题解:
每次你赶走的都是当前情况下单位时间吃的最多的牛
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <stdio.h>
#include <cstdio>
#include <string.h>
using namespace std;
typedef pair<int ,int> p;
bool cmp(p a,p b){
return a.first * b.second < b.first * a.second;
}
int main(){
int n;
cin>>n;
p cow[100005];
long long sum = 0;
long long ans = 0;
for(int i =0;i<n;i++){
cin>> cow[i].first>>cow[i].second;
sum += cow[i].second;
}
sort(cow,cow+n,cmp);
for(int i = 0;i<n;i++){
sum -= cow[i].second;
ans += sum *cow[i].first*2;
}
cout<<ans<<endl;
}
5.优先队列
1)POJ 2010
题意:
有个学校,从c个学生中挑n个学生,要求每个学生的资助金总和<=f
求符合条件的选择方案中中学生成绩中位数的最大值
题解:
有一个lower数组,记录当第i个人是中位数时候,前面的人所需要的资金的最小值,同理有一个upper数组
lower[i] = q.size() == half ? total : 0x3f3f3f3f;
total是当前最小值
if (q.size() > half)
{
total -= q.top(); q.pop();
}
因为这句话保证了当前最小
#include <iostream>
#include <algorithm>
#include <queue>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX_COW 100000 + 16
int N, C;
long long F;
pair<int, int> cow[MAX_COW];
int lower[MAX_COW], upper[MAX_COW];
int main()
{
cin >> N >> C >> F;
int half = N / 2;
for (int i = 0; i < C; ++i)
{
scanf("%d%d",&cow[i].first,&cow[i].second);
}
sort(cow, cow + C);
{
int total = 0;
priority_queue<int> q;
for (int i = 0; i < C; ++i)
{
lower[i] = q.size() == half ? total : 0x3f3f3f3f;
q.push(cow[i].second);
total += cow[i].second;
if (q.size() > half)
{
total -= q.top(); q.pop();
}
}
}
{
int total = 0;
priority_queue<int> q;
for (int i = C - 1; i >= 0; --i)
{
upper[i] = q.size() == half ? total : 0x3f3f3f3f;
q.push(cow[i].second);
total += cow[i].second;
if (q.size() > half)
{
total -= q.top(); q.pop();
}
}
}
int result = -1;
for (int i = C - 1; i >= 0; --i)
{
if (lower[i] + cow[i].second + upper[i] <= F)
{
result = cow[i].first;
break;
}
}
printf("%d\n",result);
return 0;
}
2)POJ 3614
题意:
有一些牛,每只只能涂大于等于a小于等于b spf的防晒霜,
现在我们有一些防晒霜,给出他的spf和量,求最多可以涂好多只牛
题解:
遍历每一个防晒霜,当牛的可能可以接受这个防晒霜时候,就加入队列,每次排头的是最小的那个,然后每次检测队列里面的值是不是符合要求,是就ans++,不是就再也找不出符合他的了,因为防晒霜也是从小到大spf排列的
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
typedef pair<int ,int> p;
int main(){
int c,l;
cin>>c>>l;
priority_queue <int,vector<int>,greater<int> > que;
p scow[2505];
p spf[2505];
for(int i = 0;i<c;i++){
cin>>scow[i].first>>scow[i].second;
}
for(int i = 0;i<l;i++){
cin>>spf[i].first>>spf[i].second;
}
sort(scow,scow+c);
sort(spf,spf+l);
int ans = 0;int j =0;
for(int i= 0;i<l;i++){
while(j<c&&spf[i].first >= scow[j].first){
que.push(scow[j].second);j++;
}
while(!que.empty()&&spf[i].second>0){
int x = que.top();
que.pop();
if(x<spf[i].first)continue;
ans++;
spf[i].second--;
}
}
cout<<ans<<endl;
}
6.并查集
1)POJ 2236
题意:
有n台电脑,都是坏的,现在可以执行两种操作,修好某台电脑,判断俩电脑是否相连(可传递相连)
给你电脑坐标和最大相连距离(即电脑好的且小于等于这个距离,就表示相连)
题解:
很简单,建立并查集,还有isok数组,表示电脑是否ok,对于每一个O操作都要将所有和他相连的都uint
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;
int dx[1005];
int dy[1005];
bool isok[1005];
int fa[1005];
int n, d;
int init(int n){
for(int i = 0 ;i < n;i++){
fa[i] = i;
isok[i] = false;
}
}
int find(int i){
if(i != fa[i]){
fa[i] = find(fa[i]);
}
return fa[i];
}
bool dis(int i, int j){
int a = dx[i] - dx[j];
int b = dy[i] - dy[j];
if(a*a + b*b <= d*d){
return true;
}
return false;
}
int main(){
cin >> n >> d;
init(n);
for(int i = 0;i < n; i++){
cin >> dx[i] >> dy[i];
}
char ch;
while(cin >> ch){
if(ch == 'O'){
int r;
cin >> r;
isok[r-1] = true;
for(int i = 0;i <n;i++){
if(i != r-1 && isok[i] && dis(i,r-1)){
int a = find(r-1);
int b = find(i);
fa[a] = b;
}
}
}
if(ch == 'S'){
int a,b;
cin >> a >> b;
int k,j;
k = find(a-1);
j = find(b-1);
if(k == j){
cout<<"SUCCESS"<<endl;
}
else{
cout<<"FAIL"<<endl;
}
}
}
return 0;
}
2)POJ 1703
题意:
有两个帮派,给你A信息,让你输出这两人是不是一个帮派的;给你D信息,表示这俩人不是一个帮派的,给你A信息后,每次输出你的回答
题解:
又是简单的并查集,但是很坑的就是cin会超时,然后并不知道是不是cout和scanf这种出现了不可预知到错误,然后就是init少了一个,唉,这题还是应该注意编号啊,以后就直接按编号来,别没事都从0开始
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
#define MAX_LEN 200010
int fa[MAX_LEN];
int ranks[MAX_LEN];
void init(int n){
for(int i = 0;i<n;i++){
fa[i] = i;
ranks[i] = 0;
}
}
int find(int i){
if(i == fa[i]){return i;}
else{
return fa[i] = find(fa[i]);
//return fa[i];
}
}
void uint(int a, int b){
int x = find(a);
int y = find(b);
if(x == y){return ;}
else{
if(ranks[x] > ranks[y]){
fa[y] = x;
}
else{
fa[x] = y;
if(ranks[x]==ranks[y]){
ranks[y]++;
}
}
}
}
bool same(int i,int j){
return find(i)==find(j);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(2*n+2);
//getchar();
char ch;int a,b;
while(m--){
getchar();
scanf("%c%d%d",&ch,&a,&b);
//getchar();
if(ch =='A'){
if(same(a,b)){
puts("In the same gang.");
// cout << "In the same gang." << endl;//continue;
}
else if(same(a,b+n)){
puts("In different gangs.");
// cout << "In different gangs." << endl;
//continue;
}
else{
puts("Not sure yet.");
// cout<<"Not sure yet."<<endl;
// continue;
}
}
else {
uint(a,b+n);
uint(a+n,b);
}
}
}
return 0;
}
3)AOJ 2170
题意:
有一个树,有些节点染色,每次有两种操作,第一,统计该节点到离它最近的染色父亲结点的的数值,第二,为某一个节点染色
题解:
直接记录了每一个节点的父亲节点,每一次搜索就计算到他的染色父亲结点为止
#include <iostream>
#include<stdio.h>
#define MAX_N 100005
using namespace std;
typedef long long ll;
typedef pair<int ,bool> p;
p fa[MAX_N];
int main(){
int n,q;
fa[1].first = 1;
fa[1].second = true;
while(~scanf("%d%d",&n,&q)&&n>0){
ll ans = 0;
for(int i = 2;i <= n;i++){
scanf("%d",&fa[i].first);
fa[i].second = false;
}
getchar();
for(int i = 0;i<q;i++){
char ch;int a;
scanf("%c%d",&ch,&a);
getchar();
if(ch =='Q'){
while(fa[a].second==false){
a = fa[a].first;
}
ans += a;
}
else{
fa[a].second = true;
}
}
printf("%lld\n",ans);
}
}
多校篇
1)2016.7.21 多校赛 1001
题意:已知一个n维向量,现在你找一个n维向量,向量每个元素都是a(a>=0),每个元素符号未知,求差向量长度的最小值。
题解:
可以直接展开为一元二次函数,公式最值求解
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
int main(){
int n;
cin>>n;
while(n--){
long long l;
cin>>l;
long long s[l+5];
long long sum = 0;
long long sum2 = 0;
for(int i=1;i<=l;i++){
cin>>s[i];
sum+=abs(s[i]);
sum2 +=s[i]*s[i];
}
long long p = __gcd(l*sum2 -sum*sum,l);
long long ans1 = (l*sum2 -sum*sum)/p;
long long ans2 = l/p;
cout<<ans1<<'/'<<ans2<<endl;
}
}
2)2016.7.21 多校赛 1009
题意:
一个非增数列,数字范围[0,100],已知部分数字,求前两个数/总和的最大值
题解:
先记录已知数字,特判第一第二个,其他未知位置上,未知数字用后面最靠近它的已知数字赋值,后面没有已知数字,那么就让他是0
注意:
输出使用到__gcd()库函数,求最大公约数,在头文件#include<algorithm>
中
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define pr(x) cout<<#x<<" "<<x;
#define pl(x) cout<<#x<<" "<<x<<endl;
#include<math.h>
#include<algorithm>
using namespace std;
int main(){
int n;
cin>>n;
while(n--){
int length,know;
cin>>length>>know;
int index;
int indexnum;
int s[length+5];
memset(s,0,sizeof(s));
if(know!=0){
for(int i=1;i<=know;i++){
cin>>index>>indexnum;
s[index] = indexnum;
}
}
if(length==2){cout<<1<<'/'<<1<<endl;continue;}
for(int i=1;i<=length;){
if(i==1&&s[i]==0){s[i]=100;i++;continue;}
if(i==1&&s[i]!=0){i++;}
if(i==2&&s[i]==0){s[i]=s[1];i++;continue;}
if(i==2&&s[i]!=0){i++;}
if(i>2){
if(s[i]!=0){i++;}
else{
for(int j = i+1;j<=length;){
if(s[j]==0){j++;}
else{
s[i]=s[j];break;
}
}
i++;
}
}
}
int sum=0;
for(int i=0;i<=length;i++){
sum+=s[i];
}
int p = __gcd(sum,s[1]+s[2]);
cout<<(s[1]+s[2])/p<<'/'<<sum/p<<endl;
}
}
其他
POJ 2429 关于gcd和lcm的逆求
(较大数质因数分解pollard_rho和较大数质数检测miller_rabin)
http://wenku.baidu.com/view/fbbed5a5f524ccbff12184af.html
讲两个算法的文档
题意:
给你两个数的gcd和lcm,求他们的值
如果有多组解,输出他们值和最小的那组解
题解:
对lcm/gcd这个数字质因数分解,为了让他满足gcd和lcm不变,每一种质因数只能存在于一个数字,所以先分解质因数,但对于较大的数是不是质数的判断要用miller_rabin,对一个较大数的质因数分解需要pollard_rho,返回一个大叔随机的质因数因子。
小的素数我们可以打表筛发处理,大叔检验它是不是素数只能使用miller_rabin
#include <iostream>
#include<stdio.h>
#include<string>
#include<map>
#include<vector>
#include<stdlib.h>
typedef long long ll;
using namespace std;
ll mod_mult(ll a,ll b,ll m){
ll res = 0;
ll exp = a % m;
while(b){
if(b & 1){
res += exp;
res = res % m;
}
exp <<= 1;
exp =exp % m;
b >>= 1;
}
return res;
}
ll mod_exp(ll a,ll b,ll m){
ll res = 1;
ll exp = a % m;
while(b){
if(b & 1)
res = mod_mult(res,exp,m);
exp = mod_mult(exp,exp,m);
b >>= 1;
}
return res;
}
bool miller_rabin(ll n, ll times)
{
if (n < 2) return false;
if (n == 2) return true;
if (!(n & 1)) return false;
ll q = n - 1;
int k = 0;
while (q % 2 == 0) {
k++;
q >>= 1;
}
// n - 1 = 2^k * q (q是奇素数)
// n是素数的话,一定满足下面条件
// (i) a^q ≡ 1 (mod n)
// (ii) a^q, a^2q,..., a^(k-1)q 中的某一个对n求模为-1
//
// 所以、当不满足(i)(ii)中的任何一个的时候,就有3/4的概率是合成数
//
for (int i = 0; i < times; ++i)
{
ll a = rand() % (n - 1) + 1; // 从1,..,n-1随机挑一个数
ll x = mod_exp(a, q, n);
// 检查条件(i)
if (x == 1) continue;
// 检查条件(ii)
bool found = false;
for (int j = 0; j < k; j++)
{
if (x == n - 1)
{
found = true;
break;
}
x = mod_mult(x, x, n);
}
if (found) continue;
return false;
}
return true;
}
ll get_gcd(ll n, ll m)
{
if(m == 0){return n;}
return get_gcd(m , n%m);
}
ll pollard_rho(ll n, int c)
{
ll x = 2;
ll y = 2;
ll d = 1;
while (d == 1)
{
x = mod_mult(x, x, n) + c;
y = mod_mult(y, y, n) + c;
y = mod_mult(y, y, n) + c;
d = get_gcd((x - y >= 0 ? x - y : y - x), n);
}
if (d == n) return pollard_rho(n, c + 1);
return d;
}
#define MAX_PRIME 200000
vector<int> primes;
vector<bool> is_prime;
void init_primes(){
is_prime = vector<bool>(MAX_PRIME + 1,true);
is_prime[0] = is_prime[1] = false;
for(int i =2;i<=MAX_PRIME;i++){
if(is_prime[i]){
primes.push_back(i);
for(int j = i*2;j<=MAX_PRIME;j+=i){
is_prime[j] = false;
}
}
}
}
bool isprime(ll n){
if(n <= MAX_PRIME)return is_prime[n];
else return miller_rabin(n,20);
}
void factorize(ll n,map<ll,int> &factors){
if(isprime(n)){
factors[n]++;
}
else{
for(int i = 0;i<primes.size();i++){
int p = primes[i];
while(n % p == 0){
factors[p]++;
n /= p;
}
}
if(n != 1){
if(isprime(n)){
factors[n]++;
}
else{
ll d = pollard_rho(n,1);
factorize(d,factors);
factorize(n/d,factors);
}
}
}
}
pair<ll,ll> solve(ll a,ll b){
ll c = b/a;
map <ll,int> factors;
factorize(c,factors);
vector<ll> mult_factors;
for(map<ll,int>::iterator it = factors.begin();it != factors.end();it++){
ll mul =1;
while(it -> second){
mul *= it->first;
it->second--;
}
mult_factors.push_back(mul);
}
ll best_sum = 1e18,best_x = 1,best_y = c;
for(int mask = 0;mask<(1<<mult_factors.size());mask++){
ll x = 1;
for(int i = 0;i < mult_factors.size();i++){
if(mask & (1 << i))x *= mult_factors[i];
}
ll y = c / x;
if(x < y&&x+y<best_sum){
best_sum = x+ y;
best_x = x;
best_y = y;
}
}
return make_pair(best_x*a,best_y*a);
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
init_primes();
ll a, b;
while (cin >> a >> b)
{
pair<ll, ll> ans = solve(a, b);
cout << ans.first << " " << ans.second << endl;
}
}