AYIT609暑假集训第一周上训练题题解 - 贪心和搜索
Flipping Game
来源:CodeForces - 327A
题意:
给出一个01串,要求只能翻转一次区间(在翻转的区间内,0变成1,1变成0),问翻转后1的数量最大是多少。
思路:
如果全部都为0肯定全部翻转,如果全部为1肯定只翻转一次,所以默认max应该为-1而不是-inf;
算出一段区间内0和1的个数差值cnt,与后序数字进行比较,不断更新最大值maxx和cnt。
这个别人的博客进行了时间上的优化,也可以看一下:
https://www.cnblogs.com/mqxnongmin/p/10668455.html
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stack>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(p,b) memset(p,b,sizeof(p))
#define eps 1e-9
#define PI acos(-1)
int main()
{
int n;
cin>>n;
int sum=0,cnt=0,maxx=-1;
f(i,1,n)
{
int x;
cin>>x;
if(x==0)
{
cnt++;
maxx=max(maxx,cnt);
}
else
{
cnt--;
if(cnt<0)
cnt=0;
}
if(cnt<maxx||!cnt)
sum+=x;
}
cout<<sum+maxx<<endl;
return 0;
}
Saruman's Army
来源:POJ - 3069
思路:
贪心,判断的时候不能利用区间相交来进行判断,要和灌溉那一题差不多,先进行左边的判断,避免左边部分被浪费
AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
int a[1010];
int main()
{
int r,n;
while(cin>>r>>n)
{
if(r==-1&&n==-1)
break;
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);
int i=0,ans=0;
while(i<n)
{
int l=a[i++];
// while(i<n&&a[i++]<=l+r)
while(i<n&&a[i]<=l+r)
i++;
int b=a[i-1];
while(i<n&&a[i]<=b+r)
i++;
ans++;
}
cout<<ans<<endl;
}
return 0;
}
Watering Grass
来源:UVA - 10382
题意:
给你n个喷水装置,给出其长度和宽度,接下去给出其中心和半径,问最少需要多少个喷水装置能够覆盖整条草条。
思路:
利用勾股定理求出每个装置能够到达的左区间端点和右区间端点并存在结构体中,进行排序(左区间从小到大,右区间从大到小),最后转化成区间覆盖问题即可。
特判:
排序完成后的第一个区间的左端点需要小于等于0,最大的右端点需要大于等于草条的长度;
需要考虑下一个区间的左端点小于等于当前区间的右端点,但是其右端点也小于等于该点的右区间;
利用flag进行标记,若在遍历的过程中出现中间断开,也就是说下一个区间的左端点比当前的最大右区间还要大;
考虑到7-15、13-17、15-20的这种情况,很容易把第二个算进去,本来通过变量去记录最长距离,然后去更新变量的距离和坐标,通过for循环结束,但是发现这样做右可能在测试数据上过不去,而随时随地能够终止循环的需要利用while循环来写。
AC代码:
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
struct node
{
double l;
double r;
} a[10020];
int cmp1(node x,node y)
{
if(x.l!=y.l)
return x.l<y.l;
else
return x.r>y.r;
}
//按左边界的坐标点从小到大排序,//右端点为什么不需要排序
int main()
{
std::ios::sync_with_stdio(false);
int n;
double len,w;
double c,rr;
while(cin>>n>>len>>w)
{
int num=0;
double maxxr=-1.0;
for(int i=0; i<n; i++)
{
cin>>c>>rr;
if(rr*2<=w)
{
continue;
}
else
{
double l=c-sqrt(rr*rr-w*w/4);
double r=c+sqrt(rr*rr-w*w/4);
// printf("%lf--%lf\n",l,r);
if(r>=maxxr)
{
maxxr=max(maxxr,r);
}
// if(l<=0)
// l=0;
a[num].l=l;
a[num++].r=r;
}
}
sort(a,a+num,cmp1);
int k=0;
if(a[0].l>0||maxxr<len)
{
cout<<-1<<endl;
continue;
}
double maxx=0;
int ans=0;
int flag=0;
int ww=0;
while(maxx<len)
{
double uu=maxx;
for(int i=0; i<num; i++)
{
if(a[i].l<=uu&&a[i].r>maxx)
{
// minn=a[i].l;
maxx=a[i].r;
// zz=i;
}
}
// printf("%lf----%d\n",maxx,zz);
if(uu==maxx&&uu<len)
{
ww=1;break;
}
//minn=a[zz].l;
ans++;
/*for(int i=0; i<num; i++)
{
//printf("%lf*****%lf\n",a[i].l,a[i].r);
if(a[i].l<=maxx)
{
if(a[i].r>maxx)
{
maxx=a[i].r;
ans++;
printf("%lf----%lf\n",a[i].l,a[i].r);
}
else
{
continue;
}
}
if(a[i].l>maxx)
{
flag=0;
break;
}
if(a[i].r>=len)
{
flag=1;
break;
}*/
//}//中间部分要是连接不上,处理:进行flag标记
}
// printf("%d\n",ans);
if(ww==0)
cout<<ans<<endl;
else
cout<<-1<<endl;
}
return 0;
}
Buy Low Sell High
来源:CodeForces - 867E
题意:
给出一个 \(n\) ,表示一个物品在 \(n\) 天的价格。
一开始手上没有该物品,但是可以买入或卖出来获得该物品,且只可操作一次。
问最终手上钱的最大值。
思路:贪心。
AC代码:
#include <iostream>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <stack>
#include <stdio.h>
#include <queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define inf 0x3f3f3f3f
#define mem(p,b) memset(p,b,sizeof(p))
int main()
{
int n;
sc(n);
ll w=0;
priority_queue<int,vector<int>,greater<int> > Q;
//priority_queue<int,vector<int>,greater<int>> Q; //也可以ac,但是注意规范
for(int i=1;i<=n;i++)
{
int x;
sc(x);
//Q.push(x);
if(!Q.empty()&&x>Q.top())
{
w+=x-Q.top();
Q.pop();
Q.push(x);
}
Q.push(x);
}
printf("%lld\n",w);
return 0;
}
Sticks
来源:UVA - 307
题意:
给出\(n\)根小木棍的长度,将这\(n\)根小木棒随意组合拼凑起来的木棍的长度一样且最小,
输出该长度。
思路:
需要多处剪枝。
木棍的长度一定会是这些所有木块总和的因数,而且木棍长度一定大于所有分割好的木棍中最长的木棍;
将木棍从大到小排序,因为这样可以快一点搜索出这个木棍的长度是否能够被拼出。
如果发现当前这个长度的木棍块没有办法和后面的木棍拼出来的话,那么如果i-1和当前的木棍长度相等的话就可以不判断这个木棍了;
如果拼起来的长度大于你所要的木棍长度的话,需要继续判断,因为可能和之后的可以拼在一起,所以要分情况。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stack>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(p,b) memset(p,b,sizeof(p))
#define eps 1e-9
#define PI acos(-1)
const int N=100;// 开55WA ?
int a[N],ans,cnt,n; // cnt表示已拼接的木棒数
bool book[N]; // 标记是否使用过
bool dfs(int L,int x,int xb)//当前拼接好的长度,当前拼接好的所有木棒数量,下标
{
if(xb>=n)
return 0;
if(x==cnt) //已经拼接好的==cnt
return 1;
for(int i=xb;i<n;i++)
{
if(!book[i])
{
if(L+a[i]==ans) //目前已找到的木棒正好可以拼接成所需长度
{
book[i]=1;
if(dfs(0,x+1,x)) //找下一组,若满足条件返回true
return 1;
book[i]=0;
return 0;
}
else if(L+a[i]<ans)
{
book[i]=1;
if(dfs(L+a[i],x,i+1))
return 1;
book[i]=0;
if(L==0)
return 0;
for(;a[i]==a[i+1]&&i+1<n;i++);
}
}
}
return 0;
}
bool cmp1(int x,int y)
{
return x>y;
}
int main()
{
while(cin>>n,n) // = while(cin>>n&&n)
{
//memset(book,0,sizeof(book)); 超时
for(int i=0;i<N;i++)
book[i]=0;
ans=0;
int sum=0;
for(int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
}
sort(a,a+n,cmp1);
for(ans=a[0];ans<=sum;ans++) //长度枚举
{
if(sum%ans==0)
{
cnt=sum/ans;
if(dfs(0,0,0))
break;
}
}
pr(ans);
}
return 0;
}
ROADS
来源:POJ - 1724
题意:
给出一个K,表示一个人手上最多K元钱;
给出一个N,表示N个城市;
给出一个R,表示接下来有R条路;
R条路是A B C D的形式,表示A到B的长度C,花费D。
求在K范围内,从1到N的最短路径。
思路:
法一:dfs。用邻接表建图,遍历该图更新最小步数;
法二:Dijkstra(优先队列的版本)。dis[i][j]表示从点1到点i时花费为j的最短距离,然后用优先队列时路径小的先出队。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stack>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
int head[110];
bool book[110];
int k,n,r,tot,ans;
struct node
{
int u,v,w,cost,nextt;
}e[10010];
void add(int u,int v,int w,int cost)
{
e[++tot].v=v;
e[tot].w=w;
e[tot].cost=cost;
e[tot].nextt=head[u];
head[u]=tot;
}
void dfs(int x,int ss,int cost)
{
if(cost>k||ss>ans)//钱超出了||步数超出了都不用再往下走了
return;
if(x==n)
ans=min(ans,ss);
for(int i=head[x];i!=-1;i=e[i].nextt)
{
int v=e[i].v;
if(!book[v])
{
book[v]=1;
dfs(v,ss+e[i].w,cost+e[i].cost);
book[v]=0;
}
}
}
int main()
{
tot=-1;
mem(head,-1);
cin>>k>>n>>r;
for(int i=0;i<r;i++)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
add(a,b,c,d);
}
ans=inf;
dfs(1,0,0);
if(ans==inf)
cout<<-1<<endl;
else
pr(ans);
return 0;
}
DNA sequence
来源:HDU - 1560
题意:
先给出一个T,表示接下来有T组数据;
然后给出N个DNA序列,
需要求出一个含有这N个序列的最短字符串的长度。
思路:
法一:普通搜索,如下我的代码;
法二:IDA*;
法三:搜索+哈希。
AC代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=10;
char s[N][N],DNA[4]= {'A','G','C','T'};
int n,deep,ans;
void dfs(int x,int *match)
{
if(x>deep)
return ;
int maxx=0;//只能清成0,-inf和-1不出结果
for(int i=0; i<n; i++)
{
int len=strlen(s[i]);
maxx=max(maxx,len-match[i]);
}
if(maxx==0)
{
ans=x;
return;
}
if(x+maxx>deep)
return;
for(int i=0; i<4; i++)
{
int flag=0,a[N];
for(int j=0; j<n; j++)
{
if(s[j][match[j]]==DNA[i])
{
flag=1;
a[j]=match[j]+1;
}
else
a[j]=match[j];
}
if(flag)
dfs(x+1,a);
if(ans)
return;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
deep=0;
for(int i=0; i<n; i++)
{
scanf("%s",s[i]);
int len=strlen(s[i]);
deep=max(deep,len);
}
ans=0;//0AC、-inf WA
int match[N];
memset(match,0,sizeof(match));//必须清空,不然不出结果1778ms {0}1778ms
while(1)
{
dfs(0,match);
if(ans)
break;
deep++;
}
printf("%d\n",ans);
}
return 0;
}
Infinite Maze
来源:CodeForces - 197D/196B
题意:
给出一个迷宫,只有给定的地方可以走,每个点延伸开来又是一个迷宫。 问是否可以一直的走下去。
思路:
如果可以从某一点,能走到另一个图中的某个之前已经被走过的点,就可以一直走下去,输出Yes即可。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stack>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
int n,m,x,y;
struct node
{
int x,y;
}p,q;
const int N=1520;
char s[N][N];
int book1[N][N],book2[N][N];//x轴、y轴
int to[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
bool bfs()
{
queue<node>Q;
p.x=x;
p.y=y;
book1[x][y]=x;
book2[x][y]=y;
Q.push(p);
while(!Q.empty())
{
p=Q.front();
Q.pop();
for(int i=0;i<4;i++)
{
q.x=p.x+to[i][0];
q.y=p.y+to[i][1];
int tx=(q.x%n+n)%n;
int ty=(q.y%m+m)%m;
if(s[tx][ty]!='#')
{
if(book1[tx][ty]==inf)
{
book1[tx][ty]=q.x;
book2[tx][ty]=q.y;
Q.push(q);
}
else if(book1[tx][ty]!=q.x||book2[tx][ty]!=q.y)
return 1;
}
}
}
return 0;
}
int main()
{
scc(n,m);
for(int i=0;i<n;i++)
{
scanf("%s",s[i]);
for(int j=0;j<m;j++)
{
if(s[i][j]=='S')
{
x=i;
y=j;
}
book1[i][j]=inf;
book2[i][j]=inf;
}
}
if(bfs())
printf("Yes\n");
else
printf("No\n");
return 0;
}
Ice Cave
来源:CodeForces - 540C
题意:
给出n,m,即该图大小,之后输入图。
最后两行给出起点终点,
如果走到点的话,点会变成X;
如果走到X的话,会掉下去。
问,如果掉下去的左边是终点的话,则输出YES,否则输出NO。
思路:简单BFS。
AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<list>
#include<stdlib.h>
#include<map>
#include<stack>
#include<stdio.h>
#include<queue>
using namespace std;
typedef long long ll;
#define sc(T) scanf("%d",&T)
#define scc(x,y) scanf("%d %d",&x,&y)
#define pr(T) printf("%d\n",T)
#define f(a,b,c) for (int a=b;a<=c;a++)
#define ff(a,b,c) for (int a=b;a>=c;a--)
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define PI acos(-1)
struct node
{
int x,y;
}p,q;
int to[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m,sx,sy,ex,ey;
char a[550][550];
bool bfs(int x,int y)
{
p.x=x;
p.y=y;
queue<node>Q;
Q.push(p);
while(!Q.empty())
{
p=Q.front();
Q.pop();
for(int i=0;i<4;i++)
{
q.x=to[i][0]+p.x;
q.y=to[i][1]+p.y;
if(q.x>=0&&q.x<n&&q.y>=0&&q.y<m)
{
if(a[q.x][q.y]=='X')
{
if(q.x==ex&&q.y==ey)
return 1;
}
else
{
a[q.x][q.y]='X';
Q.push(q);
}
}
}
}
return 0;
}
int main()
{
while(~scc(n,m))
{
for(int i=0;i<n;i++)
scanf("%s",a[i]);
cin>>sx>>sy>>ex>>ey;
sx--;sy--;ex--;ey--;
if(bfs(sx,sy))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
Journey
来源:CodeForces - 721C
题意:
给出地点数K、边数N和时间T(格式是x到y耗时T);
求:在时间T内,从起点1到终点N最多能走过多少个不同的点。
思路:
拓扑排序+DP。
这题的后续代码的实现自己看一下。