第一周竞赛题目解法小结
*1。机器翻译
**对于这道题其实没有什么好说的,就是一道十足的大水题(゚ー゚),运用队列加上一个判断存在的数组来快速查寻即可,需要注意的是此处的数据较小,可以只用将队列的空间开的大一些,但如果以后遇见数据较大的队列题时需要用到循环队列
一下便是实现代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define L 2000
using namespace std;
int qu[L],bus[L],h=1,t,now,num,n,m,ans;
int main()
{
freopen("translate.in","r",stdin);
freopen("translate.out","w",stdout);
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&now);
if(bus[now])continue;
ans++;
if(num<m)
{
num++;
qu[++t]=now;
bus[now]=1;
}
else
{
if(num>=m)
{
qu[++t]=now;
bus[qu[h++]]=0;
bus[now]=1;
}
}
}
printf("%d",ans);
return 0;
}
2.某龟棋
**这道题第一次做的时候想到的是深搜+剪枝,将每个格子上的分数都减去一个较大的数,然后每次深搜时若加和的值已经大于当前的答案时,就退出此次搜索,本以为会减去大量的时间,结果果断gg,只过了四个点;后来一想,这道题确实很想是到dp题啊。。。但是之所以一开始没有用dp的原因便是找不到适合表示和推演的状态。。。(这不是泪催吗。。。(ノಠ益ಠ)ノ彡┻━┻)后来经过一个ak大神的指点后思路才明了起来。。。将不同的卡牌的已用牌数牌数设为状态,有就是用数组dp[i][j][k][t]表示状态,其中i表示第一种牌,j表示第二种牌,以此类推然后当前状态dp[i][j][k][t]便是由max{dp[i][j][k][t-1],dp[i][j][k-1][t],dp[i][j-1][k][t],dp[i-1][j][k][t]}+scor[set]得来,set表示的是当前的位置(即:1+i+2*j+3*k+4*t)然后就简单了。。。答案输出dp[amount of i][amount of j][amount of k][amount of t]即可;
**题目的做法是了解了,但是此处我自己觉得仍需要总结一些关于此类dp问题的思路,今后,凡是遇见有类似此题,有多重类型和数量限制的题目(切数量不是特别大,就像此题便是),边可以将其已用或未用数量作为状态求解,切要注意类型与数量的关系,就比如在此题中个拍数与所行走到的位置的关系
以下为此题的实现代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#define M 500
using namespace std;
int n,m,dp[50][50][50][50],num[5],scor[M];
int ma(int a,int b)
{
return a>b?a:b;
}
int main()
{
freopen("tortoise.in","r",stdin);
freopen("tortoise.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&scor[i]);
for(int i=1;i<=m;i++)
{
int a;
scanf("%d",&a);
num[a]++;
}
dp[0][0][0][0]=scor[1];
for(int i=0;i<=num[1];i++)
for(int j=0;j<=num[2];j++)
for(int k=0;k<=num[3];k++)
for(int t=0;t<=num[4];t++)
{
int set=1+i+2*j+3*k+4*t;
dp[i][j][k][t]=i==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i-1][j][k][t]+scor[set]);
dp[i][j][k][t]=j==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j-1][k][t]+scor[set]);
dp[i][j][k][t]=k==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j][k-1][t]+scor[set]);
dp[i][j][k][t]=t==0?dp[i][j][k][t]:ma(dp[i][j][k][t],dp[i][j][k][t-1]+scor[set]);
}
printf("%d",dp[num[1]][num[2]][num[3]][num[4]]);
return 0;
}
3.关押罪犯
关押罪犯这道题看起来似乎有点像是图论方面的题型,但实际上可以用二分答案来解决(当然某李姓大神也发现了一种并查集的方法,而且时间发杂度是O(n),但是表示我一个弱没有听懂(。・・)ノ)
那么先复习一下二分的思路和步骤:1.得中间值,2.check一遍,3如果r>l+1,goto(1.);而此题也不例外,但是重点就在check上了,毕竟丁神说过,二分能解的题都有一定的单调性,不一定是明显的大小单调,但是这个单调性质能够使我们知道什么时候该往左边缩区间,什么时候该往右边缩区间;
而此题乍一看似乎没有特别明显的单调性,但是用二分答案的步骤来想一下,先在有m=l+r/2,则比m大的边就不能存在在图中(即不能在同一监狱中),如果满足次条件,则说明此m课以作为答案(但不一定是最小答案),则r=m;若不满足,则说明此边不是最小的最大边,答案是一个比它大的边,所以l=m;
单调性是解决了,但是如何判断比m大的边所连的点能分到两个组中使其两两不相连呢?这里便可以用到染色法;
首先声明,不是说在大于m的边中有环其一定就不成立,如一下情况:
此情况便是有环但是可以区分成两两不相连的情况;(就是因为这个在下午该题时老是调不对)
那么就要用到一个染色法(以后在判断分为两互不相连的集合时就想想这方法);运用dfs,将起点染成一种颜色,然后再将其儿子(与其相连的节点染成不同的颜色(这里可以用到异或,但要注意最好颜色用2,3表示,用1,0时就不能区分谁是没走过的,谁是染成了0这个颜色的,除非将数组的所有元素初始化为-1一类的值;))当发现本因染成不同颜色的点与自己的颜色相同时,return false,若便利完了所有的点并未出现上述情况则说明满足要求,return true;
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#define M 300000
using namespace std;
int yet[M],ind[M],sz;
int a[M],b[M],f[M],n,m;
struct node
{
int e,next;
node()
{
e=next=0;
}
}list[M];
void add(int a,int b)
{
int pre=ind[a];
ind[a]=++sz;
list[sz].e=b;
list[sz].next=pre;
}
bool dfs(int now,int f)
{
if(f!=-1)
yet[now]=yet[f]^1;
for(int i=ind[now];i;i=list[i].next)
{
if(list[i].e==f)continue;
if(yet[list[i].e]&&yet[list[i].e]!=yet[now])continue;
if(yet[list[i].e]==yet[now])return false;
if(!dfs(list[i].e,now))return false;
}
return true;
}
bool check(int now)
{
sz=0;
memset(list,0,sizeof(list));
memset(yet,0,sizeof(yet));
memset(ind,0,sizeof(ind));
for(int i=1;i<=m;i++)
if(f[i]>now)
{
add(a[i],b[i]);
add(b[i],a[i]);
}
for(int i=1;i<=n;i++)
if(!yet[i])
{
yet[i]=2;
if(!dfs(i,-1))return false;
}
return true;
}
int main()
{
freopen("prison.in","r",stdin);
freopen("prison.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d %d %d",&a[i],&b[i],&f[i]);
int l=0,r=1<<30,m;
while(l+1<r)
{
m=(l+r)/2;
if(check(m))r=m;
else l=m;
}
if(check(l))printf("%d",l);
else if(check(r))printf("%d",r);
return 0;
}
由于时间关系,这里不能将第四题总结出来,准备在下次总结中补上,对于本次考试,我还有其他关于考试技巧和知识查漏补缺的总结:
1.凡是遇见有有多类型数量限制的dp题目,想想将类型和其数量视为状态来解题
2.遇见特征不是很明显的题目时,试着用二分答案,将关于m的单调性找出,如果有这试着去用二分来解题;(一般有最小中的最大或最大中的最小时,就很有可能是二分答案)
3.判断分组时运用染色法;
4.若卡在一道题上超过十分钟都没有明显的突破点,说明思路可能有问题,试着去找其他的思路;
那么。。。就希望下次考试能吸取此次教训,再将原来学习的知识点巩固一下,多刷一些题。。。也拜托公主殿下能保佑,拜托╭( ・ㅂ・)و ̑̑