P1111 修复公路
一道运用并查集来写的题,对每次操作用并查集处理,同时进行是否是一个共同祖先的判断,每次合并两个节点,显然如果原先不连通那么合并之后联通块数量--,操作一次就对n减一次,如果n等于1了说明修复成功
#include<bits/stdc++.h>
using namespace std;
int father[10009];
int flag[10009]={1};
struct node{
int a,b,time;
};
node c[100009];
bool cmp(node a,node b){
return a.time<b.time;
}
int find(int k){
if(father[k]==k)return k;
return father[k]=find(father[k]);
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d %d %d",&c[i].a,&c[i].b,&c[i].time);
}
for(int i=1;i<=n;i++){
father[i]=i;
}
sort(c+1,c+1+m,cmp);
for(int i=1;i<=m;i++){
int fx=find(c[i].a),fy=find(c[i].b);
if(fx!=fy){
father[fx]=fy;
n--;
}
if(n==1){
printf("%d",c[i].time);
return 0;
}
}
printf("-1");
return 0;
}
P2759 奇怪的函数
x的位数=log10(x)+1;【取整】
log10(double)是cmath头文件里自带的10为底的对数
可是题目的数很大呐。
利用对数运算,可以把指数提出来,作为系数
即log(x^x)=x*log(x)
直接枚举x?试试看输入2000000000,发现过了10s才出答案两亿多
说明答案是整型范围内的,但时间效率却是O(200000000)太大了
考虑到10为底的对数函数是单调递增的,所以可以用二分答案
比起题解各种各样的方法,这个可以说是最易懂的
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
ll n;
ll left=1,right=2e9;
scanf("%lld",&n);
while(left<right){
ll mid=(left+right)/2;
ll len=(ll)(mid*log10(1.0*mid))+1;
if(len<n)left=mid+1;
else right=mid;
}
printf("%lld",left);
return 0;
}
P8800 [蓝桥杯 2022 国 B] 卡牌
由于这题是让我们求最大,所以要么用贪心做,要么用二分答案做。
显然,在这题中,如果我们无法凑出 x 套牌,那么一定也无法凑出 x+1 套牌。
所以,这题是可以用二分答案的。
由于我们二分的是牌数,所以我们可以写出下面的 check 函数
。
bool check(ll x){
ll cnt=0;
for(int i=1;i<=n;i++){
if(x-a[i]<=b[i])cnt+=max(x-a[i],(long long)0);
else return false;
}
if(cnt<=m)return true;
return false;
}
由于题目说要每种花色都有,所以如果 x−ai≤bi 时我们就可以累加需要手写的牌数了,最后在判断一下sum 是否≤m 即可。
接着我们思考一个问题,为什么只有 x−ai≤bi 时才累加sum 呢?
我们先将柿子变形一下,得到x≤ai+bi。
由于 x 表示的是要凑出 x 套牌,那么显然只有原有的牌数 ai ,加上能够手写的牌数 bi 是小于 x 的时候才能进行累加手写牌的数量的。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[200009],b[200009];
ll ans,n,m;
bool check(ll x){
ll cnt=0;
for(int i=1;i<=n;i++){
if(x-a[i]<=b[i])cnt+=max(x-a[i],(long long)0);
else return false;
}
if(cnt<=m)return true;
return false;
}
int main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
ll left=0,right=1e17;
while(left<=right){
ll mid=(left+right)/2;
if(check(mid)){
ans=mid;
left=mid+1;
}
else{
right=mid-1;
}
}
printf("%lld",ans);
return 0;
}
P1874 快速求和
这题自己是搞不定了,看看他们的优质做法吧
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
string str;
int s,n,tot;
int a[100031];
int num[55][55];
int dp[55][100031];
bool z;
signed main(){
srand(time(0));
cin>>str>>s;
n=str.size();
for(int i=0;str[i];i++){
if(str[i]!='0') z=1;
if(z) a[++tot]=str[i]-'0';
}
for(int i=1;i<=tot;i++){
num[i][i]=a[i];
for(int j=i;j-i<=11&&j<=tot;j++)
num[i][j]=num[i][j-1]*10+a[j];
}
for(int i=0;i<=tot+1;i++)
for(int j=0;j<=s+1;j++)
dp[i][j]=0x7fffffff;
dp[0][0]=0;
for(int i=1;i<=tot;i++)
for(int k=1;k<=11;k++)
if(i>=k)
for(int j=num[i-k+1][i];j<=s;j++)
dp[i][j]=min(dp[i][j],dp[i-k][j-num[i-k+1][i]]+1);
if(dp[tot][s]>tot) cout<<-1;
else cout<<dp[tot][s]-1;
return 0;
}
栈
一、栈的基本概念
1、栈的定义
栈(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈顶(Top):线性表允许进行插入删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。
栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构
2、栈的常见基本操作
InitStack(&S):初始化一个空栈S。
StackEmpty(S):判断一个栈是否为空,若栈为空则返回true,否则返回false。
Push(&S, x):进栈(栈的插入操作),若栈S未满,则将x加入使之成为新栈顶。
Pop(&S, &x):出栈(栈的删除操作),若栈S非空,则弹出栈顶元素,并用x返回。
GetTop(S, &x):读栈顶元素,若栈S非空,则用x返回栈顶元素。
#include<bits/stdc++.h>
using namespace std;
int a[100009];
int bot=0,top=0;
void push(){
scanf("%d",&a[top]);
top++;
}
void pop(){
if(top==bot){
printf("栈已空\n");
return;
}
top--;
}
void empty(){
if(top==bot){
printf("空栈\n");
}
else printf("非空");
}
int main(){
return 0;
}