1矩形覆盖(题目链接)
这个题一开始我想到的是贪心,按照横向贪和纵向贪,但是想想很明显不对。由于看到洛谷对此题的标签是搜索,我就开始了漫漫搜索路。由于数据太小,所以递归搜索可以不怎么考虑剪枝,但是我还是写了一个在搜索的第一行。搜索目标为每个点,用b数组记录各个矩形的边界,每次递归后将相应的b恢复为递归之前的值,每次递归扩展边界。这个题还有个坑就是每个矩形相互之间不能有边,点重合,所以还要加特判,特判的时候用将俩个矩形的四个角判断,判断是否在对方矩形中。具体代码如下。
#include<cmath>
#include <iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
#include<queue>
#include<bitset>
#define ll unsigned long long int
const int mod=1e9+7;
using namespace std;
int n,k;
struct node
{
int x,y;
}a[100];
struct node1
{
int lx,ly,rx,ry,cnt=0;
}b[10];
int ans=0x3f3f3f;
int check2(int x,int y,int xi,int yi,int xz,int yz)
{
if(x>=xi&&x<=xz&&y>=yi&&y<=yz)
{
return 1;
}
else
return 0;
}
int check()
{
int i,j;
for(i=0;i<n;i++)
{
int flat=0;
for(j=1;j<=k;j++)
{
if(a[i].x<=b[j].rx&&a[i].x>=b[j].lx&&a[i].y<=b[j].ry&&a[i].y>=b[j].ly)
flat=1;
}
if(flat==0)
return 0;
}
for(i=1;i<=k;i++)
{
for(j=i+1;j<=k;j++)
{
if(check2(b[i].lx,b[i].ly,b[j].lx,b[j].ly,b[j].rx,b[j].ry))
return 0;
if(check2(b[i].rx,b[i].ry,b[j].lx,b[j].ly,b[j].rx,b[j].ry))
return 0;
if(check2(b[j].lx,b[j].ly,b[i].lx,b[i].ly,b[i].rx,b[i].ry))
return 0;
if(check2(b[j].rx,b[j].ry,b[i].lx,b[i].ly,b[i].rx,b[i].ry))
return 0;
if(check2(b[i].rx,b[i].ly,b[j].lx,b[j].ly,b[j].rx,b[j].ry))
return 0;
if(check2(b[i].lx,b[i].ry,b[j].lx,b[j].ly,b[j].rx,b[j].ry))
return 0;
if(check2(b[j].rx,b[j].ly,b[i].lx,b[i].ly,b[i].rx,b[i].ry))
return 0;
if(check2(b[j].lx,b[j].ry,b[i].lx,b[i].ly,b[i].rx,b[i].ry))
return 0;
}
}
return 1;
}
void dfs(int i,int anp)
{
if(anp>ans)
return;
if(i==n)
{
if(check())
{
ans=min(ans,anp);
}
return;
}
node1 pi;
for(int j=1;j<=k;j++)
{
pi=b[j];
if(b[j].cnt==1)
{
if(a[i].x<b[j].lx)
b[j].lx=a[i].x;
if(a[i].x>b[j].rx)
b[j].rx=a[i].x;
if(a[i].y<b[j].ly)
b[j].ly=a[i].y;
if(a[i].y>b[j].ry)
b[j].ry=a[i].y;
}
else
{
b[j].lx=a[i].x;
b[j].rx=a[i].x;
b[j].ly=a[i].y;
b[j].ry=a[i].y;
b[j].cnt=1;
}
int s1=(pi.ry-pi.ly)*(pi.rx-pi.lx);
int s2=(b[j].ry-b[j].ly)*(b[j].rx-b[j].lx);
dfs(i+1,anp+s2-s1);
b[j]=pi;
}
return;
}
int main()
{
ios::sync_with_stdio(false);
ll i=0,j,m=0,sum=0;
cin>>n>>k;
for(j=0;j<=k;j++)
{
b[j].lx=b[j].rx=b[j].ly=b[j].ry=b[j].cnt=0;
}
for(i=0;i<n;i++)
{
cin>>a[i].x>>a[i].y;
}
dfs(0,0);
cout<<ans;
return 0;
}
2过河(题目链接)
这个题是个动态规划,因为每一次跳的步长不一样(原来好像做过一道每次跳的步长一样的,用二分),这个题给的整体长度1e9,特别的大,但是有石头的点非常少,跳的步长也不是很长,所以对于空开非常多的段是没有必要计算的。我们可以进行缩点,我选择的缩点范围是2520,它是10的阶乘,所以中间省去的部分无论怎么样都能跳到。看别人的题解,对于只有一种步长的进行了特判,个人认为没有必要,对b数组进行初始化以后,进行动态规划的时候会只规划这一种步长,只有满足从b[0]开始的才能规划成功。具体代码如下。
#include<cmath>
#include <iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
#include<queue>
#include<bitset>
#define ll long long int
const int mod=1e9+7;
using namespace std;
int a[1010],b[1000100],c[10000010];
int main()
{
/*ios::sync_with_stdio(false);*/
ll n,k,i=0,j,m=0,sum=0,l;
cin>>l>>n>>k>>m;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
for(i=1;i<=m;i++)
scanf("%d",&a[i]);
sort(a+1,a+m+1);
a[0]%=2520;
a[1]%=2520;
for(i=1;i<=m;i++)
{
b[i]=(a[i]-a[i-1])%2520;
}
for(i=1;i<=m;i++)
{
a[i]=a[i-1]+b[i];
c[a[i]]=1;
}
memset(b,0x3f3f3f3f,sizeof(b));
b[0]=0;
a[m+1]=(l-a[m])%2520+a[m];
for(i=1;i<=a[m+1]+k;i++)
{
for(j=n;j<=k;j++)
{
if(i>=j)
{
b[i]=min(b[i],b[i-j]+c[i]);
}
}
}
int ans=0x3f3f3f3f;
for(i=a[m+1];i<=a[m+1]+k;i++)
ans=min(ans,b[i]);
cout<<ans;
return 0;
}
3点石成金(题目链接)
做动态规划做魔怔了,遇见这种有选择性的问题就想动态规划。这个题的数据非常小,所以可以直接枚举,具体见代码。
#include<cmath>
#include <iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
using namespace std;
struct np
{
long long int a,b,c,d;
}p[100];
struct rp
{
long long int m,q;
}e[100][2];
int main()
{
long long int n,i=1,l,j,ans1=0,ans2=0,ans=0;
cin>>n;
for(i=0;i<100;i++)
p[i].a=p[i].b=p[i].c=p[i].d=0;
for(i=1;i<=n;i++)
cin>>p[i].a>>p[i].b>>p[i].c>>p[i].d;
for(i=0;i<=1<<n+1;i++)
{
j=i;
ans1=0;
ans2=0;
for(int k=0;k<20;k++)
{
if(j>>k&1)
{
ans1+=p[k].a;
ans2-=p[k].b;
if(ans2<0)
ans2=0;
}
else
{
ans1-=p[k].d;
ans2+=p[k].c;
if(ans1<0)
ans1=0;
}
}
ans=max(ans,ans1*ans2);
}
cout<<ans;
return 0;
}
4同余方程(题目链接)
这个题是一个扩展欧几里得算法的板子题,主要为了加深一下前段时间欧几里得算法和费马小定理的印象。欧几里得算法和费马小定理
这个题最终求解的是一个正数,只需要把它加b即可。代码如下。
#include<cmath>
#include <iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<set>
#include<map>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
#include<queue>
#include<bitset>
#define ll long long int
const int mod=1e9+7;
using namespace std;
ll a,b,n,m;
void gcd(ll a,ll b,ll c,ll &n,ll &m)
{
if(!b)
{
c=a;
n=1;
m=0;
}
else
{
gcd(b,a%b,c,m,n);
m-=n*(a/b);
}
return;
}
int main()
{
ios::sync_with_stdio(false);
int i,j,k;
cin>>a>>b;
gcd(a,b,-1,n,m);
while(n<0)
n+=b;
cout<<n;
return 0;
}