C:Accept
题意:给两行R,W字符串,任选起始点,上下左右走,能走过的R字符最多有多少个(不包括起始点R,且路径必须只能是R字符)。
思路:根据路径不分首尾,并且只有两行,所以我们可以简单认为从左往右走和从有往左走是等价的,只需列出从左往右的答案即可。
以s[1][1]和s[2][1]分别作为起始点出发,如果其他路上只有一个R,那很明显只有一条路,如果y+1是两个R,则考虑要不要走对角线,也可以发现,如果当前s[3-i][j]如果也是R,那么走对角线是更优的,因为到下一个列时,你所经过的R多了一个。
用dfs跑,为了防止重复跑,写个vis数组,也是避免对角线被自己循环卡死,重复访问。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e7+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M 5005
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
ll n;
string s[3];
ll ans=0;
vector<vector<bool>>vis(3,vector<bool>(N,0));
vector<vector<bool>>vis2(3,vector<bool>(N,0));
void dfs2(ll x,ll y,ll cnt)
{
if(x>2||x<1||y>n||y<1)return;
if(vis2[x][y]||s[x][y]!='R')return;
cnt++;
// cout<<x<<' '<<y<<' '<<cnt<<'\n';
vis2[x][y]=1;
ans=max(ans,cnt-1);
if(x==1)
{
if(y+1<=n&&s[x][y+1]=='R'&&s[x+1][y]=='R'&&s[x+1][y+1]=='R'&&!vis2[x+1][y])
{
dfs2(x+1,y,cnt);
}
else if(y+1<=n&&s[x][y+1]=='R')dfs2(x,y+1,cnt);
else if(s[x+1][y]=='R'&&!vis2[x+1][y])dfs2(x+1,y,cnt);
}
else
{
if(y+1<=n&&s[x][y+1]=='R'&&s[x-1][y]=='R'&&s[x-1][y+1]=='R'&&!vis2[x-1][y])
{
dfs2(x-1,y,cnt);
}
else if(y+1<=n&&s[x][y+1]=='R')dfs2(x,y+1,cnt);
else if(s[x-1][y]=='R'&&!vis2[x-1][y])dfs2(x-1,y,cnt);
}
};
void solve()
{
cin>>n;
cin>>s[1]>>s[2];
s[1]='.'+s[1];
s[2]='.'+s[2];
for(int j=1; j<=n; j++)
{
for(int i=1; i<=2; i++)
{
queue<tuple<ll,ll,ll>>q,p;
function<void(ll,ll,ll)>dfs=[&](ll x,ll y,ll cnt)
{
if(x>2||x<1||y>n||y<1)return;
if(vis[x][y]||s[x][y]!='R')return;
cnt++;
// cout<<x<<' '<<y<<' '<<cnt<<'\n';
vis[x][y]=1;
ans=max(ans,cnt-1);
if(x==1)
{
if(y+1<=n&&s[x][y+1]=='R'&&s[x+1][y]=='R'&&s[x+1][y+1]=='R'&&!vis[x+1][y])
{
dfs(x+1,y,cnt);
}
else if(y+1<=n&&s[x][y+1]=='R')dfs(x,y+1,cnt);
else if(s[x+1][y]=='R'&&!vis[x+1][y])dfs(x+1,y,cnt);
}
else
{
if(y+1<=n&&s[x][y+1]=='R'&&s[x-1][y]=='R'&&s[x-1][y+1]=='R'&&!vis[x-1][y])
{
dfs(x-1,y,cnt);
}
else if(y+1<=n&&s[x][y+1]=='R')dfs(x,y+1,cnt);
else if(s[x-1][y]=='R'&&!vis[x-1][y])dfs(x-1,y,cnt);
}
};
if(s[i][j]=='R')dfs(i,j,0);
if(s[3-i][j]=='R')
dfs2(3-i,j,0);
}
}
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
E:Accepta
题意:对于题目所给的n,找出一个小于n的数字,使得它与n的异或和,与它和n的gcd相等。
思路:
由于n(1e18)特别大,所以不考虑遍历,应该考虑二进制或者二分,二分没有找到可行性的思路,便考虑二进制,很容易发现,对于高位的二进制,必然是低位二进制的倍数,例如4是2倍数,4是2的更高位。考虑到异或其实是找出不相同的位,使其变成1,其余的变成0。思考一下,会发现其实n的二进制表达式中,最低位的1,必然是n的因子,如果要异或得到这个1,会发现很容易得到,只需要构造和n这个数只在最低位的二进制为1的为不相同便可。所以答案便是n异或它最低位的1,最后得到的数不能是0。即可。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M 5005
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
void solve()
{
ll x;cin>>x;
vector<ll>vec;
ll xx=x;
while(xx)
{
vec.pb(xx&1);
xx>>=1;
}
ll ret=0,k=0;
for(int i=0;i<vec.size()-1;i++)
{
if(vec[i])k++;
if(vec[i]&&(!ret))
{
vec[i]=0;
ret=1;
}
}
ll ans=0;
ll now=1;
for(int i=0;i<vec.size();i++)
{
ans+=vec[i]*now;
now<<=1;
}
if(k)cout<<ans<<'\n';
else cout<<-1<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
H:Accept
题意:
对于一个上下左右指令的字符串,问其中有多少个连续的字串经过指定的x,y点。
思路:
仔细观察题目,发现题目给的路径信息是经过,那么只要前缀子串经过该点,后续字串便可以随便加,不影响经过的结果。
也就是说,我们只需找到前缀字串并找到它的末尾节点即可。
枚举前缀字串的起始点i,为了在时间复杂度的要求下计算是否后续路径经过答案点,则需要开map<PLL,vector<ll>>记录最初始的长度为n的串最初都经过了那些点,并且这些点经过时的下标(因为比i前的下标所经过的点不可使用)。
对于查找以i为起始点的前缀的并经过终点的字串末尾的坐标,我们可以通过map查询,遍历到i时,终点相当于偏移了,偏移到x+(i前面那些指令所改变的x方向),y+(i前面那些指令所改变的y方向)。用map查询是否存在该点,如果存在,二分出对应的vector内第一个大于等于i的下标,得到该数字,就是答案,如果找不到则contine,让ans加上n-mp[{xx+nowx,yy+nowy}][l]+1;即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <ll,ll> pii;
const ll inff = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int N = 2e5+5;
const int mod = 998244353;
char c[N];
map <pii, vector <ll> > mp;
void solve()
{
ll n,tox,toy;
cin >> n >> tox >> toy;
ll step = 0;
ll x = 0, y = 0;
for(int i=1; i<=n; i++)
{
cin >> c[i];
if(c[i] == 'W')
y ++;
else if(c[i] == 'S')
y --;
else if(c[i] == 'A')
x --;
else
x ++;
mp[ {x,y}].push_back(i);
}
if(tox == 0 && toy == 0)
{
cout << n * (n + 1) / 2 << "\n";
return;
}
ll ans = 0;
ll tx=0,ty=0;
for(int i=1; i<=n; i++)
{
if(c[i] == 'W')
ty ++;
else if(c[i] == 'S')
ty --;
else if(c[i] == 'A')
tx --;
else
tx ++;
ll dx=tx+tox,dy=ty+toy;
ll step =lower_bound(mp[ {dx,dy}].begin(), mp[ {dx,dy}].end(), i) - mp[ {dx, dy}].begin();
if(step>0&&step<mp[{dx,dy}].size()&&mp[ {dx,dy}][step]>=i)
ans += (n - mp[ {dx,dy}][step]+1);
}
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
I:Accept
题意:给一串长度为2*n的数字,1~n分别出现两次,你可以进行多次消除,消除规则是选择相同的两个数字,将这两个数字围起来的总长度*上这个数字,便是本次消除的价值,问消除的最大价值是多少。
解题思路:我们会发现如果先处理大数,那么会受到交错区间的影响,所以转换思路,应该先从小区间算起。
所以我们开设dp数组,记录消除到某个区间右端点时的暂时最大价值。
再开一个tmp数组记录当前点为尾节点的临时最大值(我确实想不到)
用pos数组记录每个值对应的左端点和右端点。
因为要按照小区间优先规则,所以再额外开设一个up数组,并按照区间长度排序(右端点与左端点的差值)。
之后便可以for(auto [l,r]:up)从小到大遍历区间
遍历这个区间,会发现当前点tmp[l]的暂时最大值就是a[l],遍历时,从l+1->r
会发现tmp的临时最大值便是tmp[i-1]+a[l],检查当前点a[i]的左端点是否在本次遍历
的区间内,如果在里面,说明要判断哪个更优。
tmp[i]=tmp[i-1]+a[l];
ll left=pos[a[i]].ff;
if(left!=i&&left>l)
{
tmp[i]=max(tmp[i],tmp[left-1]+dp[i]);///tmp[left-1]便是上一次合成区间得到的答案,也就是前序区间的答案。
}
遍历之后,让dp[r]=tmp[r]便可以得到以r尾节点的最优值,但发现这并不会遍历到nn+1这个范围,所以我们需要在一开始的pos里面添加{0,n*2+1}这个区间。
最后输出dp[n+n+1]作为答案。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e6+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M 5005
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
bool cmp(PLL &a,PLL &b)
{
return a.ss - a.ff < b.ss - b.ff; //长度比较
}
void solve()
{
ll n;
cin>>n;
ll nn=2*n;
vector<ll>a(nn + 2);
vector<PLL>pos(n + 1);
for(int i=1;i<=nn;i++)
{
cin>>a[i];
if(pos[a[i]].ff)pos[a[i]].ss=i;
else pos[a[i]].ff=i;
}
vector<PLL>up(n+1);
pos.push_back({0,nn+1});
up=pos;
sort(up.begin(),up.end(),cmp);
vector<ll>tmp(nn+2);
vector<ll>dp(nn+2);
for(auto [l,r]:up)
{
tmp[l]=a[l];
for(int i=l+1;i<=r;i++)
{
tmp[i]=tmp[i-1]+a[l];
int left=pos[a[i]].ff;
if(left!=i&&left>l)
{
tmp[i]=max(tmp[i],tmp[left-1]+dp[i]);
}
}
dp[r]=tmp[r];
}
cout<<dp[nn+1]<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
B:
在补这道题时,复习了下最小生成树算法(克鲁斯卡尔算法和普里姆算法)
即Kruskal算法,Prim算法,Borůvka算法
在这复习一下:
小芝士:
图由两个集合V、E组成,定义为G=(V, E),其中V是顶点的有限非空集合,E是由V中顶点偶对表示的边的集合。
1:Kruskal算法:(加边法)(适用于稀疏图)时间复杂度mlogm(m是边)
将所有边根据权值排个序,由小排到大,接下来遍历这些边,选取
这条边的条件是这条边对应的两个点属于不同集合(即用并查集实现),选取后,
将两点归于同一集合中,直到所有点都属于同一个集合或者已选取n-1条边。
贴下洛谷代码:最小生成树【模板】
ll f[N];
struct node
{
ll x,y,v;
bool operator<(const node &a)const
{
return v<a.v;
}
};
ll find(ll x)
{
if(f[x]==x)return x;
else return f[x]=find(f[x]);
}
void solve()
{
ll n,m;
cin>>n>>m;
for(int i=1; i<=n; i++)
{
f[i]=i;
}
vector<node>q;
for(int i=1; i<=m; i++)
{
ll x,y,v;
cin>>x>>y>>v;
q.push_back({x,y,v});
}
sort(q.begin(),q.end());
ll sum=0;
for(auto i:q)
{
ll x=find(i.x);
ll y=find(i.y);
if(x==y)continue;
else
{
sum+=i.v;
f[x]=y;
}
}
ll cnt=0;
for(int i=1; i<=n; i++)
{
if(f[i]==i)
{
cnt++;
if(cnt==2)
{
cout<<"orz\n";///无法连通
return;
}
}
}
cout<<sum<<'\n';
}
2:prim算法:(加点法)时间复杂度为mlogm+nlogn或者n^2(n为点数)
算法从一个顶点伊始,逐渐延申覆盖联通网。
实现:开一个数组:dist记录当前所维护的联通网距离各个点的距离。
初始dist[i]全是INF(不可能达到的无穷大)
以所需要的一点作为起始点,把加入的点距离它周围点的距离计算出来,与dist数组取min,选取距离最近的点并打上标记,但是时间是O(n^2)的但很明显,是n^2而跟m没多大关系。
void prim()///朴素prim算法,适用于稠密图
{
dist[1] = 0;//把点1加入集合S,点1在集合S中,将它到集合的距离初始化为0
book[1] = true;//表示点1已经加入到了S集合中
for(int i = 2 ; i <= n ;i++)dist[i] = min(dist[i],g[1][i]);//用点1去更新dist[]
for(int i = 2 ; i <= n ; i++)
{
int temp = INF;//初始化距离
int t = -1;//接下来去寻找离集合S最近的点加入到集合中,用t记录这个点的下标。
for(int j = 2 ; j <= n; j++)
{
if(!book[j]&&dist[j]<temp)//如果这个点没有加入集合S,而且这个点到集合的距离小于temp就将下标赋给t
{
temp = dist[j];//更新集合V到集合S的最小值
t = j;//把点赋给t
}
}
if(t==-1){res = INF ; return ;}
//如果t==-1,意味着在集合V找不到边连向集合S,生成树构建失败,将res赋值正无穷表示构建失败,结束函数
book[t] = true;//如果找到了这个点,就把它加入集合S
res+=dist[t];//加上这个点到集合S的距离
for(int j = 2 ; j <= n ; j++)dist[j] = min(dist[j],g[t][j]);//用新加入的点更新dist[]
}
}
///堆优化版的prim算法,适用于稀疏图(感觉不如写Kruskal)
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PLL;
const int N=510,M=1e5+10;
int h[N],e[2*M],ne[2*M],w[2*M],idx;
int dist[N],n,m;
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int prim()
{
int cnt=0,res=0;
priority_queue<pii,vector<pii>,greater<pii>>q;
q.push({0,1});
while(q.size())
{
auto t=q.top();q.pop();
int ver=t.second,distance=t.first;
if(st[ver])continue;
st[ver]=true;
cnt++;
res+=distance;
for(int i=h[ver];~i;i=ne[i])
{
int j=e[i];
if(!st[j]&&w[i]<dist[j])
{
dist[j]=w[i];
q.push({w[i],j});
}
}
}
if(cnt!=n)return 0x3f3f3f3f;
return res;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
memset(dist,0x3f,sizeof dist);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
int ans=prim();
if(ans==0x3f3f3f3f)cout<<"impossible"<<endl;
else cout<<ans<<endl;
}
!!!很明显,边的数量不能太多,遇到完全图上面两种方式就嗝屁了。
小知识:有很少条边或弧(边的条数|E|远小于|V|²)的图称为稀疏图(sparse graph),反之边的条数|E|接近|V|²,称为稠密图(dense graph),完全图是每两个点之间都有一条对应的边。
!小知识:map有find的函数和count函数,find函数的返回值如果不是map.end(),说明是指向你查询的迭代器,count则是返回你查询内容出现的次数。虽然两者时间似乎差不多,但是相较而言,如果只是为了查找是否出现该元素,应该用find,测试时,count花费时间的时间会不稳定地超过find,之前之所以使用count的返回值是1或0来判断是否出现该元素的原因,是因为在map的count中,元素不会出现第二次,很明显,在multimap中它才更具有自己的价值。
题意:问给一些点,问能不能生成一颗最小生成树,输出价值。
解题思路:建立总边集st,查找边权的map,确认是否是当前查找的两点vis数组,
查询用的点集s。(克鲁斯卡尔算法)
重点思路:分治算法,对于k<=n^1/2的时候,可以采用枚举点集时间k^2的方法给克鲁斯卡尔塞边,对于k>n^1/2的时候考虑直接找边。
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define double long double
#define Int __int128
#define pb push_back
#define N (int)1e5+10
#define MAX_LOG 21
#define ff first
#define ss second
#define M 5005
#define ull unsigned long long
using namespace std;
const double PI=3.1415926535897932385;
const ll LLMAX=9223372036854775807ll;
const ll LLMIN=-9223372036854775808ll;
//const int MAX_INT=0x3f3f3f3f;
const int IIMAX=2147483647;
const int IIMIN=-2147483648;
const int INF=0x3f3f3f3f;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b)
{
if(b) while((a%=b)&&(b%=a));
return a+b;
}//最大公约数函数
ll spid(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}//快速幂函数
///泡沫在阳光下闪烁,像星辰在寂静得夜空中闪耀
///秋雨
// 并查集(Kruskal用)
ll f[N];
inline ll find(ll x)
{
if (x==f[x])
return f[x];
return f[x] = find(f[x]);
}
struct edge
{
ll l,r,v;
bool operator<(const edge &t) const
{
return v< t.v;
}
};
vector<edge>st; // 总边集
map<PLL, ll> mp; // 用于查找边权
ll s[N]; // 查询点集
bool vis[N]; // 需要处理的点(当询问规模过大时使用)
void solve()
{
ll n,m,q;cin>>n>>m>>q;
for (int i=1;i<=m;i++)
{
ll u,v,w;cin>>u>>v>>w;
st.push_back({u, v, w});
mp[{u,v}]=w;
}
sort(st.begin(),st.end()); // 边权从小到大排序
while (q--)
{
for(int i=1;i<=n;i++)vis[i]=0;
ll k;cin>>k;
for (int i=1;i<=k; i++)
{
cin>>s[i];
f[s[i]]=s[i];
vis[s[i]]=1;
}
vector<edge>vec; // 本轮建立MST所用到的边集
if(k<1000)///点少取点,点多取边
{
for (int l=1;l<=k;l++)
{
for (int r=1;r<=k;r++)
{
if(mp.find({s[l], s[r]})!=mp.end())
// if (mp[{s[l], s[r]}] != 0)// 直接比较会超时,需要用find
{
vec.push_back({s[l],s[r],mp[{s[l],s[r]}]});
}
}
}
sort(vec.begin(), vec.end());
}
else
{
vec=st;
}
// Kruskal模版
ll ans=0,cnt=0;
ll len=vec.size();
for(int i=0;i<len;i++)
{
ll a=vec[i].l,b=vec[i].r,w=vec[i].v;
if(vis[a]&&vis[b])
{
a=find(a),b=find(b);
if(a!=b) // 如果两个连通块不连通,则将这两个连通块合并
{
f[a]=b;
ans+=w;
cnt++;
}
}
}
if(cnt<k-1)
ans=-1;
cout<<ans<<'\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
}
///在秋天邂逅, 在春天发芽,在夏天壮大,在秋天萧瑟,在冬天萎缩,
///而后,春天再度归来。
///我觉得代码挺好理解。
A:
题意:Chino有如图所示的A和B两种不同类型的地砖Chino的住所设计成了一个n*m平面网格,她打算将其整个铺满这两种类型的瓷砖。她对瓷砖上的图案特别着迷。当相邻瓷砖上的图案完美对齐时,它们形成连续的曲线。在已经放置第一块瓷砖后,Chino好奇是否可以继续铺砖,使得恰好有K条连续曲线。如果存在这样的排列方式,你的任务是帮助她找到至少一个有效的解决方案。
解题思路:发现什么都不做,肯定有2*(n+m)条线伸向外边,一条线顶天两个端点,所以我们至少会有n+m条优美的曲线。很明显题目就是想让我们造环嘛。
我们可以按照全A排或者全B排,你会发现,就是整齐流畅的曲线了,然后你往里面塞圆,往答案上面去靠。显然,构造单位圆(上A,B下B,A)的方式是最优的(5 curves)首先根据固定的一块,按照相邻相反贪心得到左上角地板的类型和可行解的答案区间。如果有解,根据左上角类型按照 AB 交替的方式填,构造最多的单位圆。就好了。最后再根据圆数砍圆。
砍圆很明显,只会改变圆数,而不会改变向外的曲线数。大体思路就这样。