题意:
给一个n*m的图,从(1,1)走到(n,m)的花费最小值
花费的定义是 w(i,j)=(i-1)*m+j
思路:
很简单的思路 沿着边界走就是最小值了
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n,m;
cin>>n>>m;
long long res=0;
for(int i=1;i<=m;i++)
res+=i;
for(int i=2;i<=n;i++)
res+=(i*m);
cout<<res<<endl;
return ;
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
题意
给一个字符串,然后去求一个和给定字符串的长度相同的串,使得原来串加这个串后的结果是一个回文数 已知一定有解
思路:
可以去分类讨论,如果给定字符串第一位不是9,那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 9999 这样的数字
证明 以四位数为例子 最大的并且第一位不是 9 的数是 8999 如果要让他变成 9999 那么就需要 1000才能变成 9999,而1000是符合条件的 那么小于8999的四位数所需要变成 9999 的数肯定也是4位的 并且比 1000 要大的 那么就可以证明 如果给定字符串第一位不是9,那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 9999 这样的数字
第二种情况
如果第一位是9 那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 11111.... 这样的数字
比如 9999 变成 11111 就需要 1112 证明和上面差不多
然后因为这个数很大 所以我们写一个高精度减法就可以过了
#include <bits/stdc++.h>
using namespace std;
vector<int> sub(vector<int> &a,vector<int> &b)
{
vector<int> c;
for(int i=0,t=0;i<a.size()||i<b.size();i++)
{
t=a[i]-t;
if(i<b.size()) t-=b[i];
c.push_back((t+10)%10);
if(t<0) t=1;
else t=0;
}
while(c.size()>1&&c.back()==0) c.pop_back();
return c;
}
void solve()
{
string s;
int n;
cin>>n;
cin>>s;
vector<int> a;
bool st=false;
for(int i=s.size()-1;i>=0;i--)
{
a.push_back(s[i]-'0');
if(i==0)
{
if(s[i]-'0'==9)
st=true;
}
}
vector<int> b;
if(st)
{
for(int i=0;i<=n;i++)
b.push_back(1);
}
else
{
for(int i=0;i<n;i++)
b.push_back(9);
}
auto c=sub(b,a);
for(int i=c.size()-1;i>=0;i--)
cout<<c[i];
cout<<endl;
return ;
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
题意
给出一个序列a,可以做3种操作
- 选择一个i,给a[1∼i]减一
- 选择一个i,给a[i∼n]减一
- 给所有数加一 问最少多少次操作才能使所有数都变为0
思路:
把所有数都变成0就等价于它的差分数组全部变成0,那么这三个操作就等价于
差分数组为d [i]
- 减一,di+1加一
- di减一
- d1加一
可以看得出来 除了1以外的位置 只有第一个操作可以加1 除了1以外的位置 只有第二个操作可以减1 那么对于位置1以外的位置变成0的最小操作数其实是固定的,那么位置1的最小操作数也是固定的了 ( 操作完2-n位置的数然后把d[1]直接变成0就是最小操作数的总数 )
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
long long f[N];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
f[i]=a[i]-a[i-1];
long long res=0;
for(int i=2;i<=n;i++)
{
if(f[i]>0)
res+=f[i];
else if(f[i]<0)
{
res-=f[i];
f[1]+=f[i];
}
}
cout<<res+abs(f[1])<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
题意:
给出一些连续的水库,每个水库都有一个管道可以注水,速率为每秒一个单位,每个水库都有个容量vi,水位超过容量时水就会流入到其下游的其他水库,第n个水库的水溢出后会流入河里.
初始状态每个水库里都没有水,每次询问给出一个时间t,问至少要打开多少个管道才能在t时间内给n个水库都填满水.
思路:
可以想到最多就只能开n个管道 如果全开了都不能在t秒内解决问题 那就肯定不行 并且如果只开了x个管道就可以解决问题 那开x+1 ~ n 个管道肯定也可以解决问题 那么这种思路是不是就很符合二分呢?对的,就可以使用二分了
首先考虑不合法的情况
可以使用 此类推 i=1~n 充满所需要的最短时间为,取最大值判断有无解
我们能注意到,我们应该尽量打开上游的水库,这样才能尽量防止水浪费流到河里.如此开x个水龙头填满所有水库需要的时间就是⌈⌉,二分一下答案即可.
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
int a[N],ans[N];
long long s[N];
int main()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
long long maxv=0;
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+a[i];
maxv=max(maxv,(s[i]+i-1)/i);
}
cin>>m;
while(m--)
{
int x;
cin>>x;
if(x<maxv)
{
cout<<-1<<endl;
continue;
}
int l=1,r=n;
while(l<r)
{
int mid=l+r>>1;
if(1LL*x*mid>=s[n]) r=mid;
else l=mid+1;
}
cout<<r<<endl;
}
return 0;
}