A.3583
n根木棍是否能分成相等两堆。
背包dp,首先求和sum,如果为偶数就说明不行,否则考虑做一个sum/2大小的背包。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int n,w[105]; 7 while(scanf("%d",&n)!=EOF) 8 { 9 int sum=0; 10 for(int i=1;i<=n;i++)scanf("%d",&w[i]),sum+=w[i]; 11 bool dp[10005]; 12 memset(dp,0,sizeof dp); 13 dp[0]=1; 14 for(int i=1;i<=n;i++) 15 for(int j=sum/2;j>=w[i];j--) 16 dp[j]=dp[j]|dp[j-w[i]]; 17 printf("%s ",dp[sum/2]&&sum%2==0?"Yes":"No"); 18 } 19 return 0; 20 }
B.5146
单身狗在[l,r]之间且二进制中1的数量最多的数,若有多个输出最小的。
贪心,可以发现想要二进制最多,那么一定从最低位开始。
那么想要在[l,r]之间,先把l变成二进制,贪心即可(想想为什么)。
#include<bits/stdc++.h> using namespace std; #define ll __int64 int main() { int t; scanf("%d",&t); while(t--) { int a[105]={0},p=0; ll l,r,ans; scanf("%I64d%I64d",&l,&r); ans=l; while(l) { a[p++]=l&1; l>>=1; } for(int i=0;i<=60;i++) { if(a[i]==0&&ans+(1LL<<i)<=r) { ans+=1LL<<i; a[i]=1; } } printf("%I64d ",ans); } return 0; }
C.3153
n个点m条无向边,问必须经过S<=10个点再回到起点0的最短路。
dijstra+状压dp,考虑对每个S跑一边dijstra,求出S到每个点的最短路。
dp[i][j]代表状态为i,现在在j点的最短路。
#include <bits/stdc++.h> typedef long long ll; using namespace std; const int N=100005; vector<pair<int,int> > G[N]; ll d[10][N],dp[1<<10][10]; int a[10],T,n,m; void dij(int p,int u) { for(int i=0;i<n;i++) d[p][i]=1LL<<60; d[p][u]=0; queue<int> qu; qu.push(u); while(!qu.empty()) { int x=qu.front(); qu.pop(); for(int i=0;i<G[x].size();i++) { int v=G[x][i].first,c=G[x][i].second; if(d[p][v]>d[p][x]+c) d[p][v]=d[p][x]+c,qu.push(v); } } } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) G[i].clear(); for(int i=0,u,v,c;i<m;i++) { scanf("%d%d%d",&u,&v,&c); G[u].push_back({v,c}); G[v].push_back({u,c}); } scanf("%d",&m); int S=1<<m; for(int i=0;i<m;i++) scanf("%d",&a[i]),dij(i,a[i]); for(int i=0;i<S;i++) for(int j=0;j<m;j++) dp[i][j]=1LL<<60; for(int i=0;i<m;i++) dp[1<<i][i]=d[i][0]; for(int s=1;s<S;s++) { for(int i=0;i<m;i++) if((1<<i)&s) { for(int j=0;j<m;j++) if(((1<<j)&s)==0) dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+d[i][a[j]]); } } ll ans=1LL<<60; for(int i=0;i<m;i++) ans=min(ans,dp[S-1][i]+d[i][0]); printf("%I64d ",ans); } return 0; }
D.3176
n<=30个值,每个值(0,2^16),求最小需要几个数的异或后值为0,每个数只能取1次。
背包dp,考虑dp[i]代表异或值为i的最少个数。
#include<bits/stdc++.h> using namespace std; int main() { int n,x,dp[1<<16]; memset(dp,0x3f3f3f3f,sizeof dp); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); for(int j=(1<<16)-1;j>=0;j--) dp[j^x]=min(dp[j^x],dp[j]+1); dp[x]=1; } if(dp[0]==0x3f3f3f3f)printf("-1"); else printf("%d",dp[0]); }
E.3166
给一堆等级,和每个等级的区间,然后n个初始得分,每小时加1分,每个查询输出D小时后达到等级L的人数。
二分,从小到大排序,那么就是有多少个数ai+D在等级L的区间内,就相当于区间-D,然后二分左右段点。
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int a[N],n,q,d,l,L,R; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); scanf("%d",&q); while(q--) { scanf("%d%d",&d,&l); if(l==1)L=0,R=100; if(l==2)L=101,R=500; if(l==3)L=501,R=2000; if(l==4)L=2001,R=10000; if(l==5)L=10001,R=50000; if(l==6)L=50001,R=200000; if(l==7)L=200001,R=2e9+7; int x1=lower_bound(a+1,a+1+n,L-d)-a; int x2=upper_bound(a+1,a+1+n,R-d)-a; printf("%d ",x2-x1); } return 0; }
F.2704
n个点m条无向边边权为1,从1出发问距离最远的点,如有多个输出编号最小的点,再输出距离,再输出有几个点。
dijstra模板题。
#include<bits/stdc++.h> using namespace std; const int N=2e4+5; vector<int>G[N]; int n,m,u,v; void bfs() { int d[N]; memset(d,0x3f3f3f3f,sizeof d); queue<int>q; d[1]=0; q.push(1); while(!q.empty()) { int u=q.front();q.pop(); for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(d[v]>d[u]+1) { d[v]=d[u]+1; q.push(v); } } } int maxx=-1,posi=0,ans=0; for(int i=1;i<=n;i++) if(d[i]>maxx)maxx=d[i],posi=i; for(int i=1;i<=n;i++) if(d[i]==maxx)ans++; cout<<posi<<" "<<maxx<<" "<<ans; } int main() { cin>>n>>m; for(int i=0;i<m;i++) { cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } bfs(); return 0; }
G.3168
n瓶奶生产商和生产日期保证2008年,m个生产商对应奶的保质期,给你一个开始的日期,每天最多喝一瓶,问最少几瓶会过期。
哈希+贪心,把每个日期转化成天,优先处理过期时间最早的。
#include<bits/stdc++.h> using namespace std; int a[13]={0,31,29,31,30,31,30,31,31,30,31,30,31}; vector<int>v[100]; int b[600]; int main() { std::ios::sync_with_stdio(false); map<string,int>m; int id=1,n,x,y; char ch; string s; cin>>n; for(int i=1;i<=n;i++) { cin>>s; if(m[s]==0) m[s]=id++; cin>>x>>ch>>y; int day=0; for(int j=1;j<x;j++) day+=a[j]; day+=y; v[m[s]].push_back(day); } int t; cin>>t; for(int i=1;i<=t;i++) { cin>>s>>x; for(int j=0;j<v[m[s]].size();j++) b[v[m[s]][j]+x]++; } cin>>x>>ch>>y; int day=0; for(int j=1;j<x;j++) day+=a[j]; day+=y; int sum=0; for(int i=1;i<=day;i++) sum=sum+b[i]; int p=day; for(int i=day+1;i<600;i++) { while(b[i]>0) { if(p<i) p++,b[i]--; else { sum+=b[i]; break; } } } cout<<sum<<endl; return 0; }
H.3213
n<=8个题目选择k个难度总和<=p求方案数。
n很小,直接暴力dfs。
#include <bits/stdc++.h> using namespace std; int v[10],n,k,p,ans; void DFS(int pos,int s,int cnt) { if(s <= p && cnt == k) {ans++;return;} if(cnt > k || s > p) return; for(int i = pos; i < n; i++) { DFS(i + 1,s + v[i],cnt + 1); } } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&k,&p); for(int i = 0; i < n; i++) scanf("%d",&v[i]); ans = 0; DFS(0,0,0); printf("%d ",ans); } return 0; }
I.5069
给n*m的图,求一个四周全是‘.’的最大矩形。
思维,a为列前缀和,b为行前缀和,(i,j)---(i,k)那么第i行的a[i][k]-a[i][j]==k-j那么第i行j->k都是1,x=min(b[i][j],b[i][k])代表j向上或者k向上连续1的数量,然后只需要判断a[i-x+1][k]-a[i-x+1][j]==k-j,这就说明第i-x+1行j->k都是1,复杂度O(n^3)。
#include<bits/stdc++.h> using namespace std; int n,m; char G[205][205]; int a[205][205],b[205][205]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",G[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(G[i][j]=='.') a[i][j]=a[i][j-1]+1, b[i][j]=b[i-1][j]+1; else a[i][j]=0, b[i][j]=0; //(i,j) (i,k) int maxx=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { for(int k=j;k<=m;k++) { if(a[i][k]-a[i][j]!=k-j)break; if(b[i][j]&&b[i][k]) { int x=min(b[i][j],b[i][k]); while(a[i-x+1][k]-a[i-x+1][j]!=k-j)x--; maxx=max(maxx,x*(k-j+1)); } } } printf("%d",maxx); return 0; }
J.4482
n个数的积为A,m个数的积为B,求A和B的gcd。
zdragon的O((n+m)*sqrt(1e9))
求A的所有素因子,求B的所有素因子,枚举所有素因子,答案就是素因子^(该素因子A有几个,该素因子B有几个)的积。
#include <bits/stdc++.h> using namespace std; const int N=1005; const int MD=1000000000; int a[N<<2],b[N<<2],cnt; bool f; map<int,int> ma,mma; long long quick_pow(long long x,int y) { long long ans=1; while(y) { if(y&1) { ans*=x; if(ans>MD||f) ans%=MD,f=true; } x*=x; if(x>MD||f) x%=MD,f=true; y>>=1; } return ans; } int main() { int n,m; scanf("%d",&n); for(int i=0,x;i<n;i++) { scanf("%d",&x); for(int j=2;1LL*j*j<=x;j++) { if(x%j==0) { if(!ma[j]) ma[j]=(++cnt),mma[cnt]=j; while(x%j==0) a[ma[j]]++,x/=j; } } if(x>1) { if(!ma[x]) ma[x]=(++cnt),mma[cnt]=x; a[ma[x]]++; } } scanf("%d",&m); for(int i=0,x;i<m;i++) { scanf("%d",&x); for(int j=2;1LL*j*j<=x;j++) { if(x%j==0) { if(!ma[j]) ma[j]=(++cnt),mma[cnt]=j; while(x%j==0) b[ma[j]]++,x/=j; } } if(x>1) { if(!ma[x]) ma[x]=(++cnt),mma[cnt]=x; b[ma[x]]++; } } long long ans=1; for(int i=1;i<=cnt;i++) { ans=ans*quick_pow(1LL*mma[i],min(a[i],b[i])); if(ans>MD||f) ans%=MD,f=true; } if(f) printf("%09I64d ",ans); else printf("%I64d ",ans); return 0; }
wff的O(n*m)
暴力gcd。
#include <bits/stdc++.h> using namespace std; const int inf =0x3f3f3f3f; #define ll long long int a[1005],b[1005]; int main() { ll ans1=1,ans2=0; int n,m; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&a[i]); } scanf("%d",&m); for(int i=0;i<m;i++) { scanf("%d",&b[i]); } int flag=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { int t=__gcd(a[i],b[j]); ans1=1ll *ans1 * t ; if(!flag&&ans1>= 1000000000) flag=1; ans1%=1000000000; a[i]/=t; b[j]/=t; } } if(flag) printf("%09I64d",ans1% 1000000000); else printf("%I64d",ans1% 1000000000); return 0; }
K.4426
n个人初始分ai,第一名+n分,第二名+n-1分,以此类推,如果一个人i是冠军那么他的得分大于等于其他所有人的得分,问有几个人可能是冠军。
贪心,首先按ai从小到大排序,得分最高的加1分,第二高加2分,以此类推,maxx=ai+n-i+1的最大值,如果一个人i是冠军,那么ai+n大于等于maxx。
#include<bits/stdc++.h> using namespace std; const int N=3e5+5; int a[N],n,ans,sum; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=n;i>=1;i--)ans=max(ans,a[i]+n-i+1); for(int i=1;i<=n;i++)if(a[i]+n>=ans)sum++; printf("%d",sum); return 0; }
L.3457
给一个整数,输出重排列后的最小值。
贪心,首先第一个数不能为0,那么先取个不是0的最小放在第一个。然后按小到大输出。
#include<stdio.h> #include<string.h> int main() { int i,j; char a[30]; while(scanf("%s",&a)!=EOF) { int b[10]={0},min=10; for(i=0;i<strlen(a);i++) { b[a[i]-'0']++; if(a[i]-'0'<min&&a[i]-'0'!=0) min=a[i]-'0'; } printf("%d",min); b[min]--; for(i=0;i<10;i++) { for(j=0;j<b[i];j++) printf("%d",i); } printf(" "); } return 0; }
M.2609
n个点m条无向边最多k次操作使得边花费为0,求1到n的最短路。
堆优化的dijstra+dp,分成k层,dp[k][i]表示已经让k条边花费变成0到i的最短路。
#include<bits/stdc++.h> using namespace std; const int maxn=1e4+5; const int maxm=5e4+5; const int maxk=20+5; inline int read() { int x=0,f=1;char c=getchar(); for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0'; return x*f; } vector< pair<int,int> >G[maxn]; int d[maxk][maxn],vis[maxk][maxn]; int n,m,k; struct edge { int v,k,w; bool operator<(const edge& D)const{ return w>D.w; } }; int dij() { for(int i=0;i<=k;i++)for(int j=0;j<=n;j++)d[i][j]=0x3f3f3f3f,vis[i][j]=0; priority_queue<edge>q; q.push({1,0,0}); d[0][1]=0; while(!q.empty()) { edge u=q.top();q.pop(); //printf("%d %d ",u.v,u.w); if(u.v==n)return u.w; if(vis[u.k][u.v])continue; vis[u.k][u.v]=1; for(auto x:G[u.v]) { int v=x.first; int w=x.second; if(d[u.k][v]>u.w+w) q.push({v,u.k,d[u.k][v]=u.w+w}); if(u.k<k&&d[u.k+1][v]>u.w) q.push({v,u.k+1,d[u.k+1][v]=u.w}); } } return 0x3f3f3f3f; } int main() { n=read(),m=read(),k=read(); for(int i=1,u,v,w;i<=m;i++) { u=read(),v=read(),w=read(); G[u].push_back({v,w}); G[v].push_back({u,w}); } printf("%d ",dij()); return 0; }