H. Pho Restaurant
题目大意:给出 n 行只含0与1的字符串,要求调整其中每个 1 和 0 所在字符串的位置,使得每个字符串要么全为 1,要么全部为0(不能新增字符串),求最小调整次数
思路:对于每个字符串,假设其中 ‘1’ 的数量为 a,‘0’ 的数量为b,调整其中数量更少的字符一定是最优选择,即答案中一定包括min(a, b)
,并统计两种字符串数量差的最小值 res = abs(a - b)
。一个字符串在清理掉数量少的‘异类’后,变为全部为 0 或者全部为 1 的字符串。
最后如果调整次数为 0 或者全1全0的字符串皆存在,那么直接输出答案。否则说明所有字符串都是全 0 或者全 1,那么多余的 1 或者 0 放入 res 最小值所在字符串,并将答案加上 res。
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(0);
int n,ans=0,allo=false,allz=false,res=1e9;
cin>>n;
for(int i=1;i<=n;i++) {
string line;
cin>>line;
int a=count(line.begin(),line.end(),'1');
int b=line.length()-a;
res=min(res,abs(a-b));
ans+=min(a,b);
if(a>b) allo=true;
else if(b>a) allz=true;
else allo=allz=true;
}
if(ans==0||(allz&&allo)) cout<<ans<<'\n';
else cout<<(ans+res)<<'\n';
return 0;
}
C.Bit Counting Sequence
题目大意:给出一组数字
x
x
x,每个数字
x
i
x_i
xi 代表了一个数字二进制中 1 的个数,问是否存在一段连续的数字
a
a
a,使得其中每个
a
i
a_i
ai 二进制中 1 的个数等于
x
i
x_i
xi ,如果存在,则输出第一个数
a
1
a_1
a1。
思路:
对于一段给定的数组
a
1
,
a
2
,
a
3
,
.
.
.
,
a
i
−
1
,
a
i
,
.
.
.
a_1,a_2,a_3,...,a_{i-1},a_i,...
a1,a2,a3,...,ai−1,ai,...,假设其中
a
i
−
1
−
a
i
=
n
a_{i-1} - a_{i} = n
ai−1−ai=n,则
n n n | a i − 1 a_{i-1} ai−1 | a i a_{i} ai |
---|---|---|
1 1 1 | x x 011 xx011 xx011 | x x 100 xx100 xx100 |
2 2 2 | x x 0111 xx0111 xx0111 | x x 1000 xx1000 xx1000 |
3 3 3 | x x 01111 xx01111 xx01111 | x x 10000 xx10000 xx10000 |
. . . ... ... | . . . ... ... | . . . ... ... |
m − 1 m-1 m−1 | x x 011...1 ( m 个 1 ) xx011...1(m个1) xx011...1(m个1) | x x 100...0 ( m 个 0 ) xx100...0(m个0) xx100...0(m个0) |
不难发现,当 a i − 1 − a i = m − 1 a_{i-1} - a_{i} = m-1 ai−1−ai=m−1 时,符合题目要求的最小的数为
,即 a i a_i ai 个 1 加上 m m m 个 0,可以表示为 2 m + a i − 2 m 2^{m+a_i} - 2^m 2m+ai−2m,那么第一个数即 2 m + a i − 2 m − i + 1 2^{m+a_i} - 2^m - i + 1 2m+ai−2m−i+1,求出第一个数之后从前向后遍历看看每个数与给定数组的二进制1个数是否符合
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(vector<ll> a,ll x)
{
for(int i=1;i<a.size();i++) {
if(__builtin_popcountll(x)!=a[i])
return false;
x++;
}
return true;
}
void solve()
{
int n;
cin>>n;
vector<ll> a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
ll m=0,id=1;
for(int i=2;i<=n;i++) {
if(a[i-1]-a[i]+1>=m) {
m=a[i-1]-a[i]+1;
id=i;
}
}
ll ans=1-id+(1ll<<m)*((1ll<<a[id])-1);
if(ans>=0&&check(a,ans)) {
cout<<ans<<'\n';
} else {
cout<<-1<<'\n';
}
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
J.There and Back Again
题目大意:给定一个图,起点 1 终点 n,要求找出从起点走到终点并从终点返回起点的最短路径,同时从起点到终点与从终点到起点经过的边不能完全相同。
思路:
从起点到终点的道路选择最短路一定是最优解之一,因为是双向道路。跑一边dijkstra并记录路径,得到从起点开始的距离数组,根据记录的路径往回走,对于路径中的每个点枚举它能走的其他所有边,寻找最小值即可。当不存在这样的‘其他边’或者起点不能到达终点时无解。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define y second
#define x first
int h[100010],e[600010],ne[600010],w[600010],idx;
int dist[100010],pre[100010];
ll br=1e18,n,m; // 回去的路的最小长度
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra()
{
memset(dist,0x3f,sizeof(dist));
dist[1]=0,pre[1]=1;
priority_queue<pii,vector<pii>,greater<>> q;
q.push({dist[1],1});
while(!q.empty()) {
int u=q.top().y;
q.pop();
for(int i=h[u];~i;i=ne[i]) {
int j=e[i];
if(dist[j]>dist[u]+w[i]) {
pre[j]=u;
dist[j]=dist[u]+w[i];
q.push({dist[j],j});
}
}
}
}
void solve()
{
set<int> edge;
int p=n,last=0,Front=n;
do {
p=Front,Front=pre[p];
// cout<<p<<": ";
for(int i=h[Front];~i;i=ne[i]) {
int j=e[i];
if(j==p) {
edge.insert(i);
edge.insert(i^1);
}
}
for(int i=h[p];~i;i=ne[i]) {
int j=e[i];
if(edge.count(i)) continue;
// cout<<j<<' ';
br=min(br,(ll)w[i]+dist[j]+last);
}
// cout<<'\n';
last+=dist[p]-dist[Front];
} while(pre[p]!=p);
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0);
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++) {
ll a,b,c;
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
dijkstra();
solve();
if(dist[n]==0x3f3f3f3f) cout<<-1;
else cout<<(dist[n]+br>=1e18?-1:dist[n]+br)<<'\n';
return 0;
}