这道题直接纯模拟就好了,我们可以发现题意是这样的:
Need fix.
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int a[10],ans=INT_MIN,ai;
int main(){
for(int i=1;i<=4;i++)read(a[i]);
for(int i=1;i+1<=4;i++)
for(int j=i+1;j<=4;j++)
if(a[i]==a[j]){//有重复
cout<<"Need fix.";//输出
return 0;
}
for(int i=1;i<=4;i++){
if(a[i]>ans){
ans=a[i];//答案
ai=i;//编号
}
}cout<<ai;
return 0;
}
这是一道找最小值的问题,我们把读入数据代入那个公式再对所有答案去一个 min \min min 就行了,需要注意的是 ∞ \infty ∞ 设成 INT_MAX
会错,需要设成 LONG_LONG_MAX
。
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,ai;
double x,ans=LONG_LONG_MAX;//注意!
int main(){
cin>>n>>x;
for(int i=1;i<=n;i++){
double c,t,e;
cin>>c>>t>>e;
double xx=c+t*x/e;
if(xx<ans){
ans=xx;
ai=i;
}
}cout<<ai;
return 0;
}
这道题用递归做非常方便,直接模拟就行了。
满 i i i 进 1 1 1
进的位数 = = = n ÷ x n \div x n÷x
这一位的数 = = = n m o d x n \bmod x nmodx
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll n;
void work(ll n,ll x){
if(n==0)return;
work(n/x,x+1);
cout<<n%x<<" ";
}
int main(){
read(n);
if(n==0)cout<<0;//这里需要特判
work(n,1);
return 0;
}
这道题又可以用递归做。
我们发现培养之后就是斐波那契数,斐波那契数有下面几个性质:
所有自然数都可以用不重复的斐波那契数组成。
斐波那契数增长是指数级的。
所以可以直接枚举天数。
我们可以写一个 c h e c k check check 函数
bool check(ll x,ll n){
if(x<=0)return false;
if(f[x]==n)return true;
if(f[x]>n)return check(x-1,n);//这次培养的话病体数量就超过了想要培养的总数了,所有不培养
return check(x-2,n-f[x]);//否则培养
}
其他就比较简单了:
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll n,X=2,f[10010];
bool check(ll x,ll n){
if(x<=0)return false;
if(f[x]==n)return true;
if(f[x]>n)return check(x-1,n);//这次培养的话病体数量就超过了想要培养的总数了,所有不培养
return check(x-2,n-f[x]);//否则培养
}
void print(ll x,ll n){
if(f[x]==n){
cout<<X-x+1<<" ";
return;
}
if(f[x]>n)print(x-1,n);
else{
cout<<X-x+1<<" ";
print(x-2,n-f[x]);
}
}
int main(){
read(n);
if(n==0){
cout<<endl<<0;//边界
return 0;
}
if(n==1){
cout<<1<<endl<<1;//边界
return 0;
}
f[0]=1;f[1]=1;//初始化
while(1){
f[X]=f[X-1]+f[X-2];
if(check(X,n)){
print(X,n);
cout<<endl<<X;
return 0;
}X++;
}
return 0;
}
首先我们可以枚举全排列。
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,d,a[20],f[20],ans;
int gcd(int x,int y){
if(y==0)return x;
return gcd(y,x%y);
}
int check(){
ll ans=0;
for(int i=1;i<=n;i++)ans+=abs(a[f[i]]-a[f[i-1]])*abs(a[f[i]]-a[f[i-1]]);
return ans;
}
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]),f[i]=i;
do{
ans=max(ans,check());
}while(next_permutation(f+1,f+n+1));
cout<<ans;
return 0;
}
这样可以获得 50 50 50 分的好成绩
我们还可以状压 d p dp dp
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll n,d,a[15],dp[1<<20][20],ans;
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)dp[(1<<(i-1))][i]=a[i]*a[i];
for(int i=1;i<(1<<n);i++){
for(int j=1;j<=n;j++){
if(i&(1<<(j-1))){
for(int k=1;k<=n;k++){
if(i&(1<<(k-1))&&k!=j){
dp[i][j]=max(dp[i][j],dp[i^(1<<(j-1))][k]+(a[j]-a[k])*(a[j]-a[k]));
ans=max(ans,dp[i][j]);
}
}
}
}
}cout<<ans;
return 0;
}
上面 2 2 2 个代码是我考试的时候对拍用的
下面说正解了。
我们发现最优解一定是放一个最大数,再一个最小数,再一个剩下的最大数,再一下剩下的最小数……
证明的话,我觉得挺显然的qwq
Q:那你还写对拍?!
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll n,a[110],ans;
vector<ll>v;
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
a[++n]=0;
sort(a+1,a+n+1);
ll l=1,r=n;
while(l<=r){
if(l==r)v.push_back(a[l]);
else{
v.push_back(a[l]);
v.push_back(a[r]);
}l++;
r--;
}
for(int i=1;i<v.size();i++)ans=ans+abs(v[i]-v[i-1])*abs(v[i]-v[i-1]);
cout<<ans;
return 0;
}
这其实是一个背包 d p dp dp,挺裸的,主要是输出方案,这倒不算啥,关键是输出方案要符合字典序
这里借用 a p o c r y p h a l \textcolor{black}{a} \textcolor{red}{pocryphal} apocryphal 大佬的方法,把序列翻转,这样就可以了。
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
ll n,x,t[5010],a[5010],f[5010][5010],xx[5010][5010],flag;
void print(ll x,ll y){
if(x==0)return;
if(xx[x][y]){
if(flag==0)cout<<n-x+1,flag=1;
else cout<<" "<<n-x+1;
}print(x-1,y-xx[x][y]*t[x]);
}
int main(){
read(n);read(x);
for(int i=1;i<=n;i++)read(t[i]);
for(int i=1;i<=n;i++)read(a[i]);
reverse(t+1,t+n+1);
reverse(a+1,a+n+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=x;j++){
f[i][j]=f[i-1][j];
xx[i][j]=0;
if(j>=t[i]){
if(f[i-1][j-t[i]]+a[i]>=f[i][j]){
xx[i][j]=1;
f[i][j]=f[i-1][j-t[i]]+a[i];
}
}
}
}
if(f[n][x]==0){
puts("Develop failed.");
return 0;
}print(n,x);
return 0;
}
首先给出我不剪枝的代码:
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,d,a[20],f[20],ans;
int gcd(int x,int y){
if(y==0)return x;
return gcd(y,x%y);
}
bool check(){
for(int i=1;i<n;i++)
if(gcd(a[f[i]],a[f[i+1]])!=1||abs(a[f[i]]-a[f[i+1]])>d)return false;
return true;
}
int main(){
read(n);read(d);
for(int i=1;i<=n;i++)read(a[i]),f[i]=i;
do{
if(check()) ans++;
}while(next_permutation(f+1,f+n+1));
cout<<ans;
return 0;
}
只有 40 40 40 分 q w q qwq qwq。。。
再给出减枝后的代码:
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,d,ans,h[25],f[25],a[25];
vector<int>v[25];
int gcd(int x,int y){
if(x%y==0)return y;
return gcd(y,x%y);
}
void dfs(int x){
if(x==n+1){
ans++;
return;
}
for(int i:v[f[x-1]]){
if(!h[i]&&abs(a[f[x-1]]-a[i])<=d){
h[i]=1;
f[x]=i;
dfs(x+1);
h[i]=0;
}
}
}
int main(){
read(n);read(d);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(gcd(a[i],a[j])==1)v[i].push_back(j),v[j].push_back(i);
for(int i=1;i<=n;i++){
h[i]=1;
f[1]=i;
dfs(2);
h[i]=0;
}cout<<ans;
return 0;
}
这时,你拿到了 90 90 90 分的好成绩。
之后想到 100 100 100 分的话,我是听了 a p o c r y p h a l \textcolor{black}{a} \textcolor{red}{pocryphal} apocryphal 大佬的建议,写了复杂度有保证的装压 d p dp dp,时间复杂度 O ( 2 n n 2 ) O(2^n n^2) O(2nn2),比搜索的 O ( n ! ) O(n!) O(n!) 确实强了不少
十分好写,但需要一定的装压 d p dp dp 的基础
#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int n,d,a[15],dp[1<<20][20],ans;
int gcd(int x,int y){
if(x%y==0)return y;
return gcd(y,x%y);
}
int main(){
read(n);read(d);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)dp[(1<<(i-1))][i]=1;
for(int i=1;i<(1<<n);i++){
for(int j=1;j<=n;j++){
if(i&(1<<(j-1))){
for(int k=1;k<=n;k++){
if(i&(1<<(k-1))&&k!=j){
if(abs(a[j]-a[k])<=d&&gcd(a[j],a[k])==1){
dp[i][j]+=dp[i^(1<<(j-1))][k];
}
}
}
}if(i==(1<<n)-1)ans+=dp[i][j];
}
}cout<<ans;
return 0;
}
Q:我卡常搜索过了!
A:那是数据水了。
PS:这道题的数据水了,不信你给你的程序测下面的这组数据
12 2
1 1 1 1 1 1 1 1 1 1 1 1
你会发现标程过不了,因为 12 ! = 479001600 12!=479001600 12!=479001600 远超 1 1 1 亿。
这道题的 I d e a Idea Idea 不错,可是问题有点多啊
样例,毒瘤的出题人手抖了一下,把第 4 4 4 行的那个 1 1 1 打到了第 3 3 3 行,变成了
3 2
2 4
21
1
2 4
1 1 10 10
使本来就比较难懂的题目更加难懂,这里把我坑了好久。。。
数据范围,毒瘤的出题人手抖了一下,吧 1 0 5 10^5 105打成了 105 105 105。。。
数据,毒瘤的出题人没有考虑到读入数据中第 i i i 层医疗机构能管辖到的最大编号应该是单调递增的
标程,毒瘤的出题人没有设置边界条件 s 1 = 1 s_1=1 s1=1
说思路:
这题应该是一眼二分的,考虑如何 c h e c k check check。
考虑贪心
现在要做的就是快速算出两点的距离,很明显这道题的医疗机构的连接是树形的,也就是说所有医疗机构组成看一棵树,求树上任意 2 2 2 点路径,很容易想到 L C A LCA LCA,如果你不会 L C A LCA LCA 的话,可以先学一下
bool check(int x){
int k=0,m=1,a=1;
for(int i=s[D-1]+1;i<=s[D];i++){
int ans=dist(a,i);//路径长度
if(k+ans<=x)k+=ans;//我们的贪心思想
else{
m++;//新派一个医护人员
k=d[i];//这个是根节点到i的距离
if(k>x)return false;//如果的医护人员在不了x的时间内去不了i号点。
}a=i;//更新
}return m<=::m;//医护人员数量需要小于等于现有医护人员的数量
}
关于 d i s t dist dist:
void dfs(int x,int fa){
::fa[x][0]=fa;
dep[x]=dep[fa]+1;
d[x]+=d[fa];
for(int i=1;i<=20;i++)::fa[x][i]=::fa[::fa[x][i-1]][i-1];
if(x>s[D-1])return;
for(int i=l[x];i<=r[x];i++)dfs(i,x);
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=20;i>=0;i--)
if(dep[a]<=dep[b]-(1<<i))b=fa[b][i];
if(a==b)return a;
for(int i=20;i>=0;i--)
if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
int dist(int a,int b){
int k=lca(a,b);
return d[a]+d[b]-2*d[k];
}
很普通的倍增 L C A LCA LCA
现在就放一下总代码吧:
#include
using namespace std;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
const int N=1e5+10;
int sz[N],l[N],r[N],D,m,x,fa[N][25],dep[N],s[N],d[N];
void dfs(int x,int fa){
::fa[x][0]=fa;
dep[x]=dep[fa]+1;
d[x]+=d[fa];
for(int i=1;i<=20;i++)::fa[x][i]=::fa[::fa[x][i-1]][i-1];
if(x>s[D-1])return;
for(int i=l[x];i<=r[x];i++)dfs(i,x);
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=20;i>=0;i--)
if(dep[a]<=dep[b]-(1<<i))b=fa[b][i];
if(a==b)return a;
for(int i=20;i>=0;i--)
if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
return fa[a][0];
}//倍增LCA
int dist(int a,int b){
int k=lca(a,b);
return d[a]+d[b]-2*d[k];//距离
}
bool check(int x){
int k=0,m=1,a=1;
for(int i=s[D-1]+1;i<=s[D];i++){
int ans=dist(a,i);//路径长度
if(k+ans<=x)k+=ans;//我们的贪心思想
else{
m++;//新派一个医护人员
k=d[i];//这个是根节点到i的距离
if(k>x)return false;//如果的医护人员在不了x的时间内去不了i号点。
}a=i;//更新
}return m<=::m;//医护人员数量需要小于等于现有医护人员的数量
}
int main(){
read(D);read(m);
l[0]=1,r[0]=1;
s[1]=sz[1]=1;
for(int i=2;i<=D;i++)read(sz[i]),s[i]=s[i-1]+sz[i];
for(int i=1;i<D;i++){
for(int j=1;j<=sz[i];j++){
read(r[s[i-1]+j]);
r[s[i-1]+j]+=s[i];
l[s[i-1]+j]=r[s[i-1]+j-1]+1;
}
for(int j=1;j<=sz[i];j++)
for(int k=l[s[i-1]+j];k<=r[s[i-1]+j];k++)
read(d[k]);//连接k好点和他父亲节点的边的长度
}
dfs(1,0);//LCA的预处理啦
int l=0,r=INT_MAX;//二分
while(l+1<r){
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid;
}cout<<r;//输出
return 0;
}
真的的佩服 a p o c r y p h a l \textcolor{black}{a} \textcolor{red}{pocryphal} apocryphal 大佬,如果不是最后一题数据的问题的话,就直接切掉了