A 斗地主
由于数据范围不是多大,所以可以直接用搜索解决,因为这里是一个类似在二叉搜索树上求最短路的问题,故这里推荐bfs。
既然要用bfs,就需要像遍历图一样明确方向。求解这个问题需要三个人的得分情况,也就是需要讨论三个维度的方向问题,而且在遍历中需要标记数组来避免死循环,不巧的是这个数据范围(-300~300)不能让你开三维数组。但是题给了另外一个条件:a+b+c=0,所以只要求出两个人的得分,第三个人的得分就是唯一确定的了,故把方向数组和标记数组都压缩一维至二维。
对于一局斗地主,如果仅看两个人的得分情况的话,需要讨论倍数,输赢,阵营。即开局积分的倍数,是否赢下这一局,这两个人都是农民还是一个地主一个农民。对于同一种倍数的游戏,阵营分为三种情况:两农民,a农民b地主,b农民a地主,然后各自分输赢,也就是6种。而这里有3种倍数,则共有18种情况。
由于情况数较多,所以这里推荐在入队列前对状态进行标记以进行剪枝,以防过度消耗时间,同时建议对0的这个状态进行特判,这也对时间优化有利。
ll dir[30][2] = {
{0,0},{-1,2},{2,-1},{1,-2},{-2,1},{1,1},{-1,-1}, {-2,4},{4,-2},{2,-4},{-4,2},{2,2},{-2,-2}
,{-3,6},{6,-3},{3,-6},{-6,3},{3,3},{-3,-3}
};
struct node
{
ll cura, curb;
ll round;
};
bool vis[1200][1200];
ll ans = 1002;
void bfs(ll ta,ll tb,ll tc,ll limit)
{
queueq;
ta += 300, tb += 300,tc+=300;
q.push(node{ 300,300, 0});
vis[300][300] = true;
while (!q.empty())
{
node cur = q.front();
q.pop();
if (cur.round > limit)
return ;
if (cur.cura == ta && cur.curb == tb && cur.round >= 1)
{
ans = min(ans,cur.round);
}
else
{
for (int i = 1; i <= 18; i++)
{
ll nx = cur.cura + dir[i][0];
ll ny = cur.curb + dir[i][1];
//cout << nx << " " << ny << endl;
if (nx <= 600 && nx >= 0 && ny >= 0 && ny <= 600)
{
if (!vis[nx][ny])
{
vis[nx][ny] = true;
q.push(node{ nx, ny, cur.round + 1 });
}
}
}
}
}
}
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
//for (int i = 1; i <= 18; i++)
//{
// cout << dir[i][0] << " " << dir[i][1] << endl;
//}
ll n, a, b, c;
cin >> n >> a >> b >> c;
ans = 1002;
if (a == 0 && b == 0 && c == 0)
{
if (n >= 2)
{
cout << 2 << endl;
}
else
cout << -1 << endl;
}
else
{
bfs(a, b, c, n);
if (ans == 1002)
{
cout << -1 << endl;
}
else
cout << ans << endl;
}
return 0;
}
B 种树
这是一个比较麻烦的普通dp,麻烦在各种状态的讨论上。
对于连续的三棵树,题意允许的序列是:
1 2 1
1 3 1
2 1 3
2 3 1
3 1 3
2 1 2
所以需要对六种状态分别讨论。可以看出每种下一棵树都需要考虑前面那棵树的品种,所以至少需要三维的数组来描述每种情况。但是这是一个环形区域,当你种到最后一棵树,即第n棵时,n-1 n 1这三棵树也形成了一个连续序列,所以第一棵树的品种也会影响到后续的种植,故描述状态的数组还需要再加一维变成四维数组,即当前种下的是第i棵树,当前种树的品种,前一棵树的品种,第一棵树的品种四维。
然后就是在状态转移中对上述六种序列分别进行状态转移即可,由于第一棵树并没有什么特别的约束条件,并且并不影响中间的过程(除了最后一棵树),所以可以直接for循环遍历讨论。
最后取答案的时候也是需要从上述六种序列的角度来求取最大值。
ll arr[100099][4];
ll pdd[100099][4][4][4];
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
ll n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> arr[i][1] >> arr[i][2] >> arr[i][3];
pdd[1][1][2][1] = pdd[1][1][3][1] = arr[1][1];(这里是假设最后一棵树是某某品种)
pdd[1][2][3][2] = pdd[1][2][1][2] = arr[1][2];
pdd[1][3][1][3] = pdd[1][3][2][3] = arr[1][3];
for (int i = 2; i <= n; i++)
{
for (int j = 1; j <= 3; j++)
{
pdd[i][1][2][j] = pdd[i - 1][2][1][j] + arr[i][1];
pdd[i][1][3][j] = max(pdd[i - 1][3][1][j], pdd[i - 1][3][2][j]) + arr[i][1];
pdd[i][2][1][j] = max(pdd[i - 1][1][3][j], pdd[i - 1][1][2][j]) + arr[i][2];
pdd[i][2][3][j] = max(pdd[i - 1][3][2][j], pdd[i - 1][3][1][j]) + arr[i][2];
pdd[i][3][1][j] = max(pdd[i - 1][1][3][j], pdd[i - 1][1][2][j]) + arr[i][3];
pdd[i][3][2][j] = pdd[i - 1][2][3][j] + arr[i][3];
}
}
ll ans1 = max(pdd[n][2][1][1],pdd[n][2][3][3]);
ll ans2 = max({ pdd[n][1][3][3],pdd[n][1][2][2],pdd[n][1][2][3],pdd[n][1][3][2] });
ll ans3 = max({ pdd[n][3][1][1],pdd[n][3][2][2],pdd[n][3][2][1],pdd[n][3][1][2] });
cout << max({ ans1,ans2,ans3 }) << endl;
return 0;
}
D 卡片
本题是一个关于变换规律的问题。
根据变换规律,可以发现:前半部分的牌之间都隔了一张从后半部分抽来的牌。
1 2 3 4 5 6->4 1 5 2 6 3,第一张牌前面加了一张牌,第二张牌前面加了两张牌(2的前面加了4,5),第三张牌前面加了三张牌(3的前面加了4,5,6),可以推测前半部分的第n张牌前面加了n张牌,从而可知第n个数的位置变成了2*n。
那么后半部分的有什么变化规律呢?实际上也是2*n,只不过是在周期内循环而已。比如上例的4,原位置是4,扩大倍之后变成8,然后在周期内循环至2号位,再减1就是当前的位置,即2*n-(N+1)
所以可以假定某个数通过x次前半部分式的变化和y次后半部分式的变化达到最终位置K即:,x是原位置,y是指代在变化中有y次位于后半部分。
根据拓展欧几里得定理可以解出上式,再将原式变化一下得到:
,即起始位置位于x/Finalloc的元素会在m次变换后到达1号位置。再将这个式子乘以相应倍数就可以得到最终位置为K的元素的起始位置为x/FinalLoc*K.
ll quickpow(ll a, ll b, ll c)
{
ll ans = 1;
a %= c;
while (b>0)
{
if ((b & 1) > 0)
ans = (ans*a) % c;
b >>= 1;
a = (a*a) % c;
}
return ans;
}
ll exgcd(ll a, ll &x, ll b, ll &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
else
{
ll tmpgcd = exgcd(b, y, a%b, x);
y -= (a / b)*x;
return tmpgcd;
}
}
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
ll n, m, p;
cin >> n >> m >> p;
ll fpara = quickpow(2, m, n + 1);
ll spara = n + 1;
ll x, y;
ll ans = exgcd(fpara, x, spara, y);
spara /= ans; p /= ans;
x = (x*p) %((n+1)/ans);
if (x <= 0)
x += (n + 1) / ans;
cout << x << endl;
return 0;
}