https://www.luogu.org/problemnew/show/P1008
https://www.luogu.org/problemnew/show/P2668
这道题明显就是一个大暴力,但是考场上碰到这种题确实让人伤脑筋,
如何在考场上稳稳地拿下这100分呢?
首先,为了进行完全的搜索,这题应用DFS的结构,
而不是贪心选择某种出牌方式。然后我们应先分析搜索的方法。在DFS中我们先考虑不带牌的出法。即单顺子,双顺子,三顺子。
三带一、三带二可以看做是特殊的三张牌;
四带二可以看做是特殊的炸弹。
通过归纳,剩下的出牌方法就只有单排,对子和王炸了。
所以在DFS的单层中,程序需要做:
搜索单顺子,双顺子,三顺子
搜索三张牌,炸弹
搜索剩下的单排,对子,王炸
这样写的话可能只能通过60%~80%的数据。考虑优化
三张牌和炸弹带的牌可以最后在剩下的单排,对子中去除。
而不用在搜索三张牌,炸弹时进行搜索。
https://www.luogu.org/problemnew/show/P2540
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
#define INF 2147483647
using namespace std;
struct sth
{
int l[20];
int ld,zd,w;
bool operator ==(const sth y)const
{
if (ld!=y.ld) return false;
if (zd!=y.zd) return false;
if (w!=y.w) return false;
for (int i=0;i<20;i++)
if (l[i]!=y.l[i]) return false;
return true;
}
}p,debug;
int t,n,i,j,x,y,ans;
int w,bs,sm;
bool cl(sth s)
{
for (int k=3;k<=15;k++)
if (s.l[k]>0) return false;
return true;
}
void make(sth s,int step)
{
if (cl(s))
{
step+=bool(s.w);
if (step<ans) ans=step;
return;
}
int k,l,m,h;
sth f=s;
for (k=3;k<=10;k++)
{
l=0;
for (m=k;m<=14;m++)
{
if (f.l[m]>0) l++; else break;
if (l>=5)
{
for (h=k;h<=m;h++)
f.l[h]--;
make(f,step+1);
f=s;
}
}
}
for (k=3;k<=12;k++)
{
l=0;
for (m=k;m<=14;m++)
{
if (f.l[m]>1) l++; else break;
if (l>=3)
{
for (h=k;h<=m;h++)
f.l[h]-=2;
make(f,step+1);
f=s;
}
}
}
for (k=3;k<=13;k++)
{
l=0;
for (m=k;m<=14;m++)
{
if (f.l[m]>2) l++; else break;
if (l>=2)
{
for (h=k;h<=m;h++)
f.l[h]-=3;
make(f,step+1);
f=s;
}
}
}
for (k=3;k<=15;k++)
{
if (f.l[k]>2)
{
f.l[k]-=3;
f.ld++;
make(f,step+1);
f=s;
}
}
for (k=3;k<=15;k++)
{
if (f.l[k]>3)
{
f.l[k]-=4;
f.zd++;
make(f,step+1);
f=s;
}
}
int dan=0,dui=0;
for (k=3;k<=15;k++)
switch(f.l[k])
{
case 1:dan++;break;
case 2:dui++;break;
case 3:dan++;dui++;break;
case 4:dui+=2;break;
}
if (f.w) dan+=f.w;
while (f.zd>0 && (dan>1||dui>0))
{
if (dui>1) dui-=2;
else if (dan>1) dan-=2;
else if (dui==1) dui-=1;
f.zd--;
}
dan+=dui;
while (f.ld>0&&dan>0)
{
f.ld--;
dan--;
}
if (dan>=f.w) dan-=f.w; else f.w=0;
for (k=3;k<=15;k++)
f.l[k]=0;
make(f,step+dan);
}
void init()
{
debug.l[3]=1;
debug.l[11]=1;
debug.w=2;
debug.l[12]=2;
debug.ld=3;
debug.zd=1;
}
int main()
{
// init();
scanf("%d%d",&t,&n);
for (i=1;i<=t;i++)
{
memset(p.l,0,sizeof(p.l));
w=0;
for (j=1;j<=n;j++)
{
scanf("%d%d",&x,&y);
if (x==1||x==2) x+=13;
if (x==0) w++;
else p.l[x]++;
}
p.w=w;
ans=n;
make(p,0);
printf("%d\n",ans);
}
return 0;
}
前缀和 pre[i]=pre[i-1]+a[i]
二维前缀和
pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j]
pre[x2][y2]-pre[x1-1][y2]-pre[x2][y1-1]+pre[x1-1][y1-1]
x1,y1
x2,y2
https://www.luogu.org/problemnew/show/P3417
第i个动作的第j位向后的最近一个数字k的位置
if k == a[i][j][k+1]:
suf[i][j][k]=k+1
else:
suf[i][j][k]=suf[i][j][k+1]