2015 7月 做题记录
1 CF 546D:(数论,dp
题意:实际上是统计小于5e6范围内所有数字的质因子数目。
解法:先用筛法求出范围内所有素数(开始t了一次以为是因为裸的素数筛太慢,后来换成线性筛还是不行,,结果是因为脑残用了cincout)。然后就是dp的思想,任何一个合数的质因子数目等于它本身除以一个质因子之后的数字的质因子数目加一。
线性筛见此文:http://blog.csdn.net/dinosoft/article/details/5829550
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; typedef pair<int,int> P; const ll maxv=5e6+40; int t; int A,B; int p[maxv]; int a[maxv]; int dp[maxv]; ll ans[maxv]; vector<int> pri; void init(){ for(int i=2;i<maxv;i++){ if(!p[i]){ pri.pb(i); } for(int j=0;j<pri.size()&&(ll)pri[j]*i<maxv;j++){ int k=pri[j]; p[k*i]++; a[k*i]=k; if(i%k==0) break; } } dp[1]=0; for(int i=2;i<maxv;i++){ if(p[i]) dp[i]=dp[i/a[i]]+1; else dp[i]=1; } for(int i=1;i<maxv;i++){ ans[i]=ans[i-1]+dp[i]; } } int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); init(); cin>>t; while(t--){ scanf("%d%d",&A,&B); printf("%lld\n",ans[A]-ans[B]); } return 0; }
2 CF 545E:(最短路
题意:求一个“路径树”,在这个树中任意两点间的最短路与原图中相同,输出边权和最小的路径树。
思路:直接求最短路,路径长度相同的就换较短的一条边。
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=3e5+40; struct edge{ ll f,t; ll w,o; }; int n,m; vector<edge> G[maxv]; edge es[maxv]; ll dis[maxv]; int ans[maxv]; priority_queue<P,vector<P>,greater<P> > Q; void dij(int s){ memset(dis,0x2f,sizeof dis); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ ll u=Q.top().second; ll d=Q.top().first; Q.pop(); if(d>dis[u]) continue; for(int i=0;i<G[u].size();i++){ edge &e=G[u][i]; if(dis[u]+e.w<dis[e.t]){ dis[e.t]=dis[u]+e.w; Q.push(P(dis[e.t],e.t)); ans[e.t]=e.o; }else if(dis[u]+e.w==dis[e.t]){ if(e.w<es[ans[e.t]].w) ans[e.t]=e.o; } } } } int u; int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>m; for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); es[i]=(edge){u,v,w,i}; G[u].pb((edge){u,v,w,i}); G[v].pb((edge){v,u,w,i}); } cin>>u; dij(u); ll tol=0; for(int i=1;i<=n;i++){ if(i!=u){ tol+=es[ans[i]].w; } } cout<<tol<<endl; for(int i=1;i<=n;i++){ if(i!=u) printf("%d ",ans[i]); } return 0; }
3 HDU 1083:(最大重叠子区间
题意:给若干个区间,然后每次选择若干不相交的区间去掉,问一共要操作几次。
思路:本来是想用排序然后每次贪心选的,WA了若干次。。。。然后查了题解说是不能直接贪心,要求最大重叠区间的数目,但是我还没有想出反例来。。。。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=205; int T,n; P a[maxv*2]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ cin>>n; for(int i=0;i<n;i++){ int s,t; scanf("%d%d",&s,&t); if(s>t) swap(s,t); a[i*2]=P((s-1)/2,+1); a[i*2+1]=P((t-1)/2+1,-1); } sort(a,a+2*n); int ans=0; int in=0; for(int i=0;i<2*n;i++){ in+=a[i].second; ans=max(ans,in); } printf("%d\n",ans*10); } return 0; }
题目:前n个数的一个排列但是数字之间没有空格,要求恢复这个排列中的空格。
思路:爆搜,不过不太好写,我先把每个数字可能出现的位置预处理了一遍,然后从最大的数字向最小的数字搜索,之所以从大数字开始是因为两位数出现的位置肯定比一位数出现的要少。
#include<iostream> #include<algorithm> #include<vector> #include<functional> #include<bitset> #include<string> #include<map> #include<cstring> #include<cstdio> #define pb push_back using namespace std; typedef long long ll; string s; map<string,int> msi; map<int,string> mis; bool m[150]; void init(){ for(int i=1;i<=55;i++){ string a; if(i<10) a.pb(i+'0'); else{ a.pb(i/10+'0'); a.pb(i%10+'0'); } msi[a]=i; mis[i]=a; } } int ans[150]; vector<int> pos[150]; bool dfs(int num){ if(num==0) return true; vector<int> &p=pos[num]; for(int i=0;i<p.size();i++){ int len=(num>9?2:1); int k=p[i]; if(len==1&&!m[k]){ ans[k]=num; m[k]=1; if(dfs(num-1)){ return true; } ans[k]=0; m[k]=0; }else if(len==2&&!m[k]&&!m[k+1]){ ans[k]=ans[k+1]=num; m[k]=m[k+1]=1; if(dfs(num-1)){ return true; } ans[k]=ans[k+1]=0; m[k]=m[k+1]=0; } } return false; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); init(); cin>>s; int maxx=s.size(); if(maxx>9){ maxx=9+(maxx-9)/2; } for(int i=1;i<=maxx;i++){ for(int j=0;j<s.size();j++){ int len=(i>9?2:1); if(j+len<=s.size()){ if(s.substr(j,len)==mis[i]){ pos[i].pb(j); } } } } dfs(maxx); for(int i=0;i<s.size();i++){ cout<<ans[i]; if(i!=s.size()-1) cout<<" "; if(ans[i]>9) i++; } return 0; }
5 CF 547B(栈
题目:连续子序列的特征值是序列中最小的值,现在询问所有长度为x的子序列的特征值的最大值是多少。
思路:用栈处理上下界。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e5+30; int n; int a[maxv]; stack<P> s; int low[maxv],up[maxv]; int ans[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n; for(int i=0;i<n;i++) scanf("%d",a+i); low[0]=0; s.push(P(a[0],0)); for(int i=1;i<n;i++){ low[i]=i; while(!s.empty()&&s.top().first>=a[i]){ low[i]=low[s.top().second]; s.pop(); } s.push(P(a[i],low[i])); } up[n-1]=n-1; while(!s.empty()) s.pop(); s.push(P(a[n-1],n-1)); for(int i=n-2;i>=0;i--){ up[i]=i; while(!s.empty()&&s.top().first>=a[i]){ up[i]=up[s.top().second]; s.pop(); } s.push(P(a[i],up[i])); } for(int i=0;i<n;i++){ int len=up[i]-low[i]+1; ans[len]=max(ans[len],a[i]); } for(int i=n-1;i>=0;i--){ ans[i]=max(ans[i],ans[i+1]); } for(int i=1;i<=n;i++){ printf("%d ",ans[i]); } return 0; }
6 HDU 5282(dp(注意!
题目:给出两个字符串,设其最长公共子序列的长度为L,求x有长度为L的Y的子序列。
思路:dp,感觉很巧妙。首先最长公共子序列dp跑一遍,然后再有一个dp,状态是f[i][j]表示x的前i个字符中与y的前j个字符组成的长度为dp[i][j]的子序列有多少个。状态转移: 见代码。到达f[i][j]有两种状态,一种是不选当前a[i]一种是选。不过具体转移还是不太好想出的。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back using namespace std; typedef long long ll; const int maxv=1e3+50; const int mod=1e9+7; int T; string a,b; ll dp[maxv][maxv],f[maxv][maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ cin>>a>>b; memset(dp,0,sizeof dp); memset(f,0,sizeof f); for(int i=0;i<maxv;i++){ f[0][i]=f[i][0]=1; } int sa=a.size(),sb=b.size(); for(int i=1;i<=sa;i++){ for(int j=1;j<=sb;j++){ if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1; else{ dp[i][j]=max(dp[i][j],dp[i-1][j]); dp[i][j]=max(dp[i][j],dp[i][j-1]); } } } for(int i=1;i<=sa;i++){ for(int j=1;j<=sb;j++){ if(dp[i][j]==dp[i-1][j]) f[i][j]=(f[i][j]+f[i-1][j])%mod; int p=j; while(p>0&&a[i-1]!=b[p-1]) p--; if(p>0){ if(dp[i-1][p-1]+1==dp[i][j]){ f[i][j]=(f[i][j]+f[i-1][p-1])%mod; } } } } cout<<f[sa][sb]<<endl; } return 0; }
7 POJ 3977(折半枚举
题目:给出n个数,要求求出一个其和的绝对值最小的子集的绝对值。不能为空。
思路:折半枚举,但是代码能力简直太渣竟然实现不出。。。。参考了这篇题解:
http://www.hankcs.com/program/algorithm/poj-3977-subset.html
实现方面,首先pair的比较大小是first作为第一关键字,second作为第二关键字,所以可以把答案直接写成pair,比较起来简洁很多。
其次,这个显然可以把集合的和作为key,把所用数目作为值放到map里,比数组排序再用lowerbound简洁很多。再者,折半的时候第一部分用数据结构存,第二部分不需要存,在计算的同时用第一部分的数据直接查找解即可。然后是lowerbound的查找结果是以这个数作为lowerbound的最小的数,所以可能的值只有两个而不是三个,注意细节可以减少代码量。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=40; ll a[maxv]; map<ll,int> mli; int n; ll labs(ll a){ return a>0?a:-a; } int main(){ ////freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n,n){ mli.clear(); for(int i=0;i<n;i++){ scanf("%lld",a+i); } P res(labs(a[0]),1); int num=n/2; for(int i=1;i<1<<num;i++){ int cont =0; ll sum=0; for(int j=0;j<num;j++){ if(i&(1<<j)){ cont++; sum+=a[j]; } } res=min(res,P(labs(sum),cont)); if(mli.find(sum)!=mli.end()){ mli[sum]=min(cont,mli[sum]); }else{ mli[sum]=cont; } } for(int i=1;i<(1<<(n-num));i++){ ll sum=0; int cont=0; for(int j=0;j<n-num;j++){ if(i&(1<<j)){ sum+=a[num+j]; cont++; } } res=min(res,P(labs(sum),cont)); map<ll,int>::iterator it=mli.lower_bound(-sum); if(it!=mli.end()){ res=min(res,P(labs(it->first+sum),it->second+cont)); } if(it!=mli.begin()){ it--; res=min(res,P(labs(it->first+sum),it->second+cont)); } } cout<<res.first<<" "<<res.second<<endl; } return 0; }
8 CF 547C(容斥原理
题目:给出若干数,然后若干组询问,每次询问一个数,如果这个数存在,那么删去这个数,如果没有,添加这个数,每次询问后输出此时已经有的数中互质的有多少对。
思路:利用容斥原理求出已经有的数中与正在询问的数不互质的数的数目。先对所有数进行质数分解。然后通过含有的不同质数对所有数字进行分类。之后就可以用容斥原理统计数目。因为质数的数目实际上不大(每个数的质因子数目实际上很小)所以此类题可以直接用dfs。
PS:此题暴露出了在数学题上的若干细节问题,改了一上午。。。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=5e5+400; vector<int> G[maxv]; vector<int> pri; bool p[maxv]; set<int> beer; int f[maxv],c[maxv],a[maxv]; int n,q; void init(){ for(int i=2;i<maxv;i++){ if(!p[i]) pri.pb(i); for(int j=0;j<pri.size()&&i*pri[j]<maxv;j++){ p[i*pri[j]]=1; if(i%pri[j]==0) break; } } } void getp(){ for(int i=0;i<n;i++){ int x=a[i]; int h=0; while(x>1&&pri[h]*pri[h]<=x){ if(x%pri[h]==0){ G[i].pb(pri[h]); while(x>1&&x%pri[h]==0) x/=pri[h]; } h++; } if(x!=1) G[i].pb(x); } } void read(){ cin>>n>>q; for(int i=0;i<n;i++) scanf("%d",a+i); } ll ans=0; int top=0; void dfs(int t,int x,int cont,int flag,int val){ if(t>=(int)G[x].size()){ if(cont==0) return; ans+=f[val]*flag; c[top++]=val; return; } dfs(t+1,x,cont,flag,val); dfs(t+1,x,cont+1,flag*-1,val*G[x][t]); } void solve(){ int x; while(q--){ scanf("%d",&x); x--; if(a[x]==1){ if(beer.find(x)!=beer.end()){ beer.erase(x); ans-=beer.size(); }else{ ans+=beer.size(); beer.insert(x); } }else{ if(beer.find(x)!=beer.end()){ beer.erase(x); ans-=beer.size(); top=0; dfs(0,x,0,-1,1); ans--; beer.erase(x); for(int i=0;i<top;i++) f[c[i]]--; }else{ top=0; ans+=beer.size(); dfs(0,x,0,1,1); beer.insert(x); for(int i=0;i<top;i++) f[c[i]]++; } } printf("%lld\n",ans); } } int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); init(); read(); getp(); solve(); return 0; }
9 CF 544D(图,YY题
题目:给一个图,每个点之间的路径长度都是1,现在给出两个源点和终点,求最多删去多少边,使得这两对源点和汇点的距离均小于给定的值。
思路:用bfs求任意两点之间的最短距离,然后枚举这两条路径的重复部分。
(wa了好久好久好久。。。。。更新最短路的时候把反向的最短路也一块更新了,导致无限wa。。。。为什么呢。。因为所有的最短路算法都是单源的!(除了floyd),所以无向图也要写成有向图。。如果把反向的最短距离一起更新的话,只求一个源点的最短路或许没事,但是多个源点在同一个图上跑的话可能导致某些路径提早结束更新(!!!)。
#include<bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=3040; int n,m; vector<int> G[maxv]; int dis[maxv][maxv]; int s1,t1,l1,s2,t2,l2; queue<int> Q; void bfs(int u){ dis[u][u]=0; Q.push(u); while(!Q.empty()){ int v=Q.front(); Q.pop(); for(int i=0;i<G[v].size();i++){ int vv=G[v][i]; if(dis[u][vv]>dis[u][v]+1){ dis[u][vv]=dis[u][v]+1; Q.push(vv); } } } } void solve(){ if(dis[s1][t1]>l1||dis[s2][t2]>l2){ cout<<-1<<endl; return; } int ans=m-dis[s1][t1]-dis[s2][t2]; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(dis[s1][i]+dis[i][j]+dis[j][t1]<=l1&&dis[s2][j]+dis[i][j]+dis[i][t2]<=l2) ans=max(ans,m-dis[s1][i]-dis[t2][i]-dis[i][j]-dis[s2][j]-dis[j][t1]); if(dis[s1][i]+dis[i][j]+dis[j][t1]<=l1&&dis[s2][i]+dis[i][j]+dis[j][t2]<=l2){ ans=max(ans,m-dis[s1][i]-dis[s2][i]-dis[i][j]-dis[j][t2]-dis[j][t1]); } } } cout<<ans<<endl; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>m; memset(dis,0x3f,sizeof dis); for(int i=0;i<m;i++){ int a,b; scanf("%d%d",&a,&b); G[a].pb(b); G[b].pb(a); } cin>>s1>>t1>>l1>>s2>>t2>>l2; for(int i=1;i<=n;i++) bfs(i); solve(); return 0; }
10 CF 558C(状态预处理
题目:若干数,每次操作可以乘2或者除2,问最少多少次操作能把所有数变成一样。
思路:bfs处理出每个数通过操作能到达的数字的步长,然后计算所有数字都能到达的数字的最小总步数。。。自己竟然都没想到bfs。。简直。。。
#include<bits/stdc++.h> #define pb push_back #define se second #define fs first using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1e5+400; int a[maxv],n; int ans[maxv]; int cn[maxv]; bool vis[maxv]; queue<P> Q; void bfs(int x){ Q.push(P(x,0)); cn[x]++; memset(vis,0,sizeof vis); vis[x]=1; while(!Q.empty()){ int y=Q.front().fs; int c=Q.front().se; Q.pop(); if(y*2<maxv&&!vis[y*2]){ cn[y*2]++; ans[y*2]+=c+1; vis[y*2]=1; Q.push(P(y*2,c+1)); } if(y/2>0&&!vis[y/2]){ cn[y/2]++; ans[y/2]+=c+1; vis[y/2]=1; Q.push(P(y/2,c+1)); } } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n; for(int i=0;i<n;i++) scanf("%d",a+i); sort(a,a+n,greater<int>()); for(int i=0;i<n;i++){ bfs(a[i]); } int anss=1e9; for(int i=1;i<=maxv;i++){ if(cn[i]==n){ anss=min(anss,ans[i]); } } cout<<anss<<endl; return 0; }
11 poj 1990(树状数组
题目:每个牛有一个听力值,任意两个牛相互交流时需要以二者的最大听力值交谈,交谈的费用为听力值乘距离。问所有牛两两交流的总费用。
思路:树状数组,原先看题解写过一次,今天再看竟然又不会。。。。没想到排序,然后是没想出两个BIT。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e4+30; struct BIT{ int a[maxv]; void add(int p,int x){ while(p<maxv){ a[p]+=x; p+=p&-p; } } ll sum(int p){ ll ans=0; while(p>0){ ans+=a[p]; p-=p&-p; } return ans; } }A,B; int N; P cow[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>N; for(int i=1;i<=N;i++){ int x,y; scanf("%d%d",&x,&y); cow[i-1]=P(x,y); } sort(cow,cow+N); ll ans=0; for(int i=0;i<N;i++){ int vol=cow[i].fs; int pos=cow[i].se; int pern=A.sum(pos); int postn=i-pern; ans+=vol*(pern*pos+B.sum(maxv-1)-2*B.sum(pos)-postn*pos); A.add(pos,1); B.add(pos,pos); } cout<<ans<<endl; return 0; }
12 poj 1201(差分约束
题目:给出若干区间,要求一个集合,集合在每个区间中的元素数目不小于一个值,求最小集合的大小。
思路:差分约束,或者线段树维护区间中已经选择的数的个数,然后尽量往后选。差分约束写的时候被卡vector邻接表了。。。t了好久最后写了个难看的链式结构过掉了。。。
差分约束:
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=5e4+400; struct E{ int t,w; E *next; }es[maxv*4]; int h=0; E *G[maxv]; E *tail[maxv]; void add(int from,int to,int w){ tail[from]->next=&es[h++]; tail[from]->t=to; tail[from]->w=w; tail[from]=tail[from]->next; tail[from]->next=NULL; } int n; int dis[maxv]; int s=maxv,t=0; bool vis[maxv]; queue<int> Q; void BF(int s){ for(int i=s;i<=t;i++) dis[i]=-1e9; dis[s]=0; Q.push(s); vis[s]=1; while(!Q.empty()){ int v=Q.front(); Q.pop(); vis[v]=0; for(E *i=G[v];i->next!=NULL;i=i->next){ E &e=*i; if(dis[e.t]<dis[v]+e.w){ dis[e.t]=dis[v]+e.w; if(!vis[e.t]){ vis[e.t]=1; Q.push(e.t); } } } } cout<<dis[t]<<endl; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n; for(int i=0;i<maxv;i++){ G[i]=&es[h++]; tail[i]=G[i]; tail[i]->next=NULL; } for(int i=0;i<n;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); s=min(s,x); t=max(t,y+1); add(x,y+1,z); } for(int i=0;i<maxv-1;i++){ add(i,i+1,0); add(i+1,i,-1); } BF(s); return 0; }
PS:邻接表换成list也Tle。。。。list到底要你何用。。。。。。
树状数组实现:
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=5e4+300; struct BIT{ int a[maxv]; void add(int p,int x){ while(p<maxv){ a[p]+=x; p+=p&-p; } } int sum(int p){ int ans=0; while(p>0){ ans+=a[p]; p-=p&-p; } return ans; } int interSum(int a,int b){ return sum(b)-sum(a-1); } }B; int n; struct Data{ int a,b,c; }read[maxv]; bool cmp(Data a,Data b){ return a.b<b.b; } int used[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n; int ans=0; for(int i=0;i<n;i++){ scanf("%d%d%d",&read[i].a,&read[i].b,&read[i].c); read[i].a++; read[i].b++; } sort(read,read+n,cmp); for(int i=0;i<n;i++){ int a=read[i].a,b=read[i].b,c=read[i].c; int need=c-B.interSum(a,b); if(need<=0){ continue; }else{ for(int i=b;i>=a;i--){ if(!need) break; if(used[i]) continue; else{ B.add(i,1); used[i]=1; ans++; need--; } } } } cout<<ans<<endl; return 0; }
13 poj 3368(rmq
题目:给出一个递增数列,询问某个区间内出现最多的数字出现的次数是多少。
思路:首先它是递增的,其次,这个询问并没有问出现次数最多的数字是什么,那么其实数字是什么无关紧要,只需要关心相同的数字出现了多少次就好了。然后这个题几乎就是一个裸的rmq。。。。。以前看题解做了依次,今天自己竟然又想了半天。。。。。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1e5+400; struct Node{ int num; int l,r; int rangel,ranger; Node *ch[2]; }; Node *root; int sz; vector<int> a; int sum[maxv]; void build(Node *&n,int l,int r){ n=new Node(); n->r=r,n->l=l; if(r-l<=1){ n->num=a[l]; n->rangel=sum[l]-a[l]+1; n->ranger=sum[l]+1; }else{ Node *&chr=n->ch[1],*&chl=n->ch[0]; build(chl,l,(l+r)/2); build(chr,(r+l)/2,r); n->num=max(chr->num,chl->num); n->rangel=chl->rangel; n->ranger=chr->ranger; } } int query(Node *n,int a,int b){ int l=n->rangel,r=n->ranger; if(n->r-n->l<=1) return min(b,r)-max(a,l); Node *chr=n->ch[1],*chl=n->ch[0]; if(l>=b||r<=a) return -1; if(a<=l&&b>=r) return n->num; else return max(query(chl,a,b),query(chr,a,b)); } int n,q; int re[maxv]; void read(){ int last=-1e9; a.clear(); for(int i=0;i<n;i++){ scanf("%d",re+i); if(re[i]!=last){ a.pb(1); last=re[i]; }else{ a[a.size()-1]++; } } sum[0]=a[0]; for(int i=1;i<a.size();i++) sum[i]=sum[i-1]+a[i]; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n>>q,n){ read(); sz=a.size(); build(root,0,sz); while(q--){ int x,y; scanf("%d%d",&x,&y); cout<<query(root,x,y+1)<<endl; } } return 0; }
14 poj 2155(二维BIT
题目:一个矩阵,每次将一个给定矩形内的点翻转。然后询问某个点的状态。
思路:裸的二维树状数组。思路和一维情况一致,二维bit比我原先想的要好写。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1005; struct BIT2D{ ll a[maxv][maxv]; void add(int x,int y,int n){ for(int i=x;i<maxv;i+=i&-i){ for(int j=y;j<maxv;j+=j&-j){ a[i][j]+=n; } } } ll sum(int x,int y){ ll ans=0; for(int i=x;i>0;i-=i&-i){ for(int j=y;j>0;j-=j&-j){ ans+=a[i][j]; } } return ans; } void clear(){ for(int i=0;i<maxv;i++){ for(int j=0;j<maxv;j++){ a[i][j]=0; } } } }B; int N,X,T; int main(){ freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>X; while(X--){ cin>>N>>T; B.clear(); while(T--){ char c[3]; scanf("%s",c); if(c[0]=='Q'){ int x,y; scanf("%d%d",&x,&y); if(B.sum(x,y)%2){ puts("1"); }else{ puts("0"); } }else{ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); B.add(x1,y1,1); B.add(x2+1,y2+1,1); B.add(x1,y2+1,-1); B.add(x2+1,y1,-1); } } if(X!=0) puts(""); } return 0; }
15 cf 399D(概率dp
题目:给出一个方形网格,给某些格子染色,然后每次随机涂色,问涂满需要的期望步数是多少。
思路:逆向dp,按照剩余的未涂色的行数和列数,从涂满的位置开始dp,概率转移用方程求得。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #include<cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e3+30; int n,m; bool cp[maxv],rp[maxv]; double dp[maxv][maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>n>>m; int r=n,c=n; for(int i=0;i<m;i++){ int x,y; scanf("%d%d",&x,&y); if(!rp[x]) r--; if(!cp[y]) c--; rp[x]=cp[y]=1; } for(int i=1;i<maxv;i++) dp[i][0]=dp[i-1][0]+n/(double)i; for(int j=1;j<maxv;j++) dp[0][j]=dp[0][j-1]+n/(double)j; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ double &v=dp[i][j]; v=dp[i-1][j-1]*(i)*(j) +dp[i-1][j]*(i)*(n-j) +dp[i][j-1]*(n-i)*(j); v/=(double)n*n; v++; v/=1.0-(n-i)*(n-j)/(double)(n*n); } printf("%.10f",dp[r][c]); return 0; }
16 CF 559C(dp
题目:给出一个棋盘和若干不能经过的点,有多少种走法从左上走到右下。
思路:做不出这种dp哎。。。。以到达某个黑点的合法路径数为状态dp,具体的dp实际上是枚举不合法路径上的第一个黑点的位置,这样不会重复,然后减一下
#include<bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2045; const ll mod=1e9+7; const int maxn=1e6+40; ll fac[maxn],inv[maxn]; ll qpow(ll a,ll p){ ll ans=1; ll xx=a; while(p>0){ if(p&1) ans=(xx*ans)%mod; xx=(xx*xx)%mod; p>>=1; } return ans; } void init(){ fac[0]=1; inv[0]=1; for(ll i=1;i<maxn;i++){ fac[i]=(fac[i-1]*i)%mod; inv[i]=inv[i-1]*qpow(i,mod-2)%mod; } } int h,w,n; P a[maxv]; ll dp[maxv]; ll culC(ll a,ll b){ return fac[a]*inv[a-b]%mod*inv[b]%mod; } ll path(ll sx,ll sy,ll tx,ll ty){ return culC(ty-sy+tx-sx,tx-sx); } void solve(){ for(int i=0;i<=n;i++){ ll ans=0; for(int j=0;j<i;j++){ if(a[j].se<=a[i].se) ans+=path(a[j].fs,a[j].se,a[i].fs,a[i].se)*dp[j]%mod,ans%=mod; } dp[i]=(path(1,1,a[i].fs,a[i].se)-ans)%mod+mod,dp[i]%=mod; } } int main(){ ///freopen("/home/files/CppFiles/in","r",stdin); init(); cin>>h>>w>>n; for(int i=0;i<n;i++){ int c,r; scanf("%d%d",&r,&c); a[i].fs=r; a[i].se=c; } sort(a,a+n); a[n]=P(h,w); solve(); cout<<dp[n]<<endl; return 0; }
17 HDU 4082(杂题,暴力
题目:给出若干个点,求以这些点顶点的相似三角型的最大个数。
思路:暴力枚举。。。恶心题有重点,共线处理没弄好,然后就是浮点数的二维排序(pair)似乎导致了问题。
#include<bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.0000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=20; typedef pair<double,double> pdd; int n; int x[maxv]; int y[maxv]; pair<double,double> a[1000000]; double dis(int i,int j){ return sqrt((double)sq(x[i]-x[j])+sq(y[i]-y[j])); } bool same(pdd a,pdd b){ if(abs(a.fs-b.fs)<0.000000001&&abs(a.se-b.se)<0.000000001) return 1; return 0; } double culk(int x1,int y1,int x2,int y2){ return (double)(y1-y2)/(double)(x1-x2); } set<P> S; int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n,n){ S.clear(); int h=0; int ii=0; for(;ii<n;ii++){ cin>>x[ii]>>y[ii]; if(S.find(P(x[ii],y[ii]))!=S.end()){ n--; ii--; }else{ S.insert(P(x[ii],y[ii])); } } for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ for(int k=j+1;k<n;k++){ if(abs(culk(x[i],y[i],x[j],y[j])-culk(x[i],y[i],x[k],y[k]))<eps) continue; double p[3]; p[0]=dis(i,j),p[1]=dis(j,k),p[2]=dis(i,k); if(abs(p[0])<eps||abs(p[1])<eps||abs(p[2])<eps) continue; sort(p,p+3); a[h++]=pdd(p[1]/p[0],p[2]/p[0]); } } } pdd last=pdd(-1,-1); ll ans=0; for(int i=0;i<h;i++){ ll cont=0; for(int j=0;j<h;j++){ if(same(a[i],a[j])){ cont++; } } ans=max(ans,cont); } cout<<ans<<endl; } return 0; }
18 CF 540D(概率dp
题目:石头剪刀布三种生物,给出初始数量,随机碰撞,问最后只有某种生物存活的概率分别为多少。
思路:之前做过的统计期望步数的题目需要考虑状态到自身的转移,但是这个题并不需要考虑这个,只有状态发生转移时才会产生影响。。。恩。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; int r,s,p; double dp[102][102][102]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>r>>s>>p){ memset(dp,0,sizeof dp); dp[r][s][p]=1; for(int i=r;i>=0;i--){ for(int j=s;j>=0;j--){ for(int k=p;k>=0;k--){ double d=(i*j+i*k+j*k); if(d==0) continue; if(i>0) dp[i-1][j][k]+=dp[i][j][k]*i*k/d; if(j>0) dp[i][j-1][k]+=dp[i][j][k]*i*j/d; if(k>0) dp[i][j][k-1]+=dp[i][j][k]*j*k/d; } } } double ans1=0; for(int i=1;i<=100;i++){ ans1+=dp[i][0][0]; } double ans2=0; for(int i=1;i<=100;i++){ ans2+=dp[0][i][0]; } double ans3=0; for(int i=1;i<=100;i++){ ans3+=dp[0][0][i]; } printf("%.10f %.10f %.10f",ans1,ans2,ans3); } return 0; }
19 HDU 4162(字符串的最小表示
题目:求与字符串循环同构的最小串。
解:http://blog.csdn.net/ljd4305/article/details/38116909
关于算法正确性的思考:
首先如果k!=0,那么必然有i和j后的k个字符均相等。此时,不妨假设a[i+k]<b[j+k],那么j开头的串必然不是最小串,并且j到j+k区间开头的串均不可能是最小串(与i开头串比较可得),故j+=k+1,又最小串在这个过程中不可能错过,因为当i或j增加时要么已经证明对应区间不是最小值,要么只增加1,所以一定会遇到最小值,又因为每次最小值一定不会被淘汰,故此算法是正确的。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #define pb push_back #define fs first #define se second using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=3e5*2+30000; char chain[maxv]; int a[maxv]; int len; int getans(){ int i=0,j=1,k=0; while(i+k<len&&j+k<len){ if(a[i+k]!=a[j+k]){ if(a[i+k]<a[j+k]){ j+=k+1; k=0; }else{ i+=k+1; k=0; } }else{ k++; } if(i==j) j++; } return i>j?j:i; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(scanf("%s",chain)!=EOF){ len=strlen(chain); if(len==0) return 0; for(int i=0;i<len;i++){ if(i==len-1){ a[i]=(chain[0]-chain[len-1]+8)%8; continue; } a[i]=(chain[i+1]-chain[i]+8)%8; } int ans=getans(); for(int i=ans;i<len;i++){ printf("%d",a[i]); } for(int i=0;i<ans;i++){ printf("%d",a[i]); } puts(""); } return 0; }
20 HDU 4193(单调队列
题目:一个数列的循环等价中有多少的所有前缀都是大于0的。
解:单调队列,单调队列的应用特征(区域内的值不变,滑窗),此题把数列首尾连接后对于每一个数列内的前缀和实际上就是减一下就好,大小关系与位置无关,所以可以运用单调队列维护区间最小值,自己随便看了点单调队列的思路就开始写,后来发现当时根本没有看懂。。。。。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #include<cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) using namespace std; typedef long long ll; typedef pair<ll,ll> P; int n; const int maxv=2e6+30; int a[maxv]; deque<P> Q; int sum[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ while(cin>>n,n){ Q.clear(); for(int i=1;i<=n;i++){ scanf("%d",a+i); a[n+i]=a[i]; } for(int i=1;i<=2*n;i++){ sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++){ while(!Q.empty()&&Q.back().fs>sum[i]) Q.pop_back(); Q.push_back(P(sum[i],i)); } int ans=0; for(int i=n+1;i<=2*n;i++){ while(i-Q.front().se>n) Q.pop_front(); if(Q.front().fs-sum[i-n-1]>=0) ans++; while(!Q.empty()&&Q.back().fs>sum[i]) Q.pop_back(); Q.push_back(P(sum[i],i)); } cout<<ans<<endl; } return 0; }
21 HDU 4254(条件概率
题目:一个袋子中有n个球,均为蓝色或红色,假设有0~n个红球的概率均等,现在从袋子里取出了p个球,其中有q个红球,问若再取一个球取出的是红球的概率是多少。
解:有一个一行公式版本的解法,但是证明实在长的不想看。。。。可以枚举总共的红球的数目,然后求出固定红球数目时得到相应实验结果的概率,再求出此时再取一个为红球的概率,那么最后的答案就是每种情况下最后取出红球的概率之和/每种情况下得到相应实验结果的概率之和。
计算概率的时候可以利用对数在线性时间内得到阶乘值(想起了斯特林公式。。。)。
22 HDU 4260(递归
题目:给出汉诺塔游戏的一个局面,问最少用多少步走完。
解:就是个简单递归,校内赛的时候卡题了结果心态不好哎。。。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #include<cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) using namespace std; typedef long long ll; typedef pair<ll,ll> P; string p; char other(char a,char b){ for(char i='A';i<='C';i++){ if(i!=a&&i!=b) return i; } } ll solve(int x,char to){////把前x个盘子搬到柱子to上 int i=x; for(;i>=1;i--){ if(p[i-1]!=to){ break; } } if(i==0) return 0; return solve(i-1,other(to,p[i-1]))+(1LL<<(i-1)); } int main(){ ////freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ while(cin>>p){ if(p[0]=='X') break; int len=p.size(); cout<<solve(len,'B')<<endl; } return 0; }
22 HDU 4283(区间dp
题目:一个队列中的每个人有一个容忍度,总怒气等于每人的怒气之和,每个人的怒气等于前面的人数乘容忍度,现通过一个栈调整顺序,使怒气值最小。
思路:区间dp,状态为区间i,j内的最小花费,那么以区间内的第一个数的最终位置进行转移,这个数后面的与前面的不相干,因为第一个出栈后前面的必然都已经出栈。然后花费要用前缀和计算。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #include<cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=105; int T; int n; int cas=0; int d[maxv]; ll dp[maxv][maxv]; int sum[maxv]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ cas++; cin>>n; for(int i=0;i<maxv;i++){ for(int j=0;j<maxv;j++){ if(i>=j) dp[i][j]=0; else dp[i][j]=(1<<25); } } for(int i=1;i<=n;i++){ scanf("%d",d+i); sum[i]=sum[i-1]+d[i]; } for(int k=2;k<=n;k++){ for(int i=1;i+k-1<=n;i++){ int j=i+k-1; for(int m=1;m<=k;m++){ dp[i][j]=min(dp[i][j],dp[i+1][i+m-1]+(m-1)*d[i]+dp[i+m][j]+m*(sum[j]-sum[i+m-1])); } } } printf("Case #%d: %lld\n",cas,dp[1][n]); } return 0; }
23 HDU 5335(bfs,对角线
题目:从一个01图的左上角走到右下角走出的最小二进制数。
思路:有一个要点是从起点的路径长度只与x+y的值有关,所以通过x+y的值增加就可以逐渐增加路径长度不重复。
#include<iostream> #include<map> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<vector> #include<queue> #include<stack> #include<functional> #include<set> #include<cmath> #include<iomanip> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1005; int T,n,m; int G[maxv][maxv]; bool mark[maxv][maxv]; bool vis[maxv][maxv]; char str[maxv]; int far=0; queue<P> Q; const int drx[4]={0,1,-1,0}; const int dry[4]={1,0,0,-1}; void bfs(){ memset(mark,0,sizeof mark); memset(vis,0,sizeof vis); Q.push(P(1,0)); while(!Q.empty()){ int x=Q.front().fs,y=Q.front().se; Q.pop(); for(int i=0;i<4;i++){ int dx=x+drx[i],dy=y+dry[i]; if(dx<1||dx>n||dy<1||dy>m) continue; if(!vis[dx][dy]){ vis[dx][dy]=1; if(G[dx][dy]==1||(dx==n&&dy==m)){ mark[dx][dy]=1; far=max(far,dx+dy); }else if(G[dx][dy]==0){ Q.push(P(dx,dy)); } } } } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ memset(G,-1,sizeof G); cin>>n>>m; for(int i=1;i<=n;i++){ scanf("%s",str); for(int j=1;j<=m;j++){ G[i][j]=str[j-1]-'0'; } } far=0; bfs(); if(mark[n][m]){ printf("%d\n",G[n][m]); continue; } for(int dia=far;dia<=m+n;dia++){ int minn=1; for(int x=1;x<=n;x++){ int y=dia-x; if(y>=1&&y<=m&&mark[x][y]){ minn=min(minn,G[x][y]); } if(minn==0) break; } for(int x=1;x<=n;x++){ int y=dia-x; if(y>=1&&y<=m&&G[x][y]==minn&&mark[x][y]){ mark[x+1][y]=mark[x][y+1]=1; } } printf("%d",minn); } puts(""); } return 0; }
24 HDU 4313(最小生成树
题目:给出一棵数还有树上的若干个危险点,现要求删除一些边使得任意两个危险点均不联通。求删除的最小权值和。
思路:删除的另一个方向考虑的话就是(添加最大权值的边使得任意危险点不连通),可以使用类似kruskall的方法贪心加边。
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1e5+4000; int fa[maxv]; int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } bool same(int x,int y){ return find(x)==find(y); } void uni(int x,int y){ if(same(x,y)) return; fa[find(x)]=find(y); } struct EDGE{ int from,to,cost; EDGE(int f,int t,int c):from(f),to(t),cost(c){}; EDGE(){} }es[maxv]; bool cmp(EDGE a,EDGE b){ return a.cost>b.cost; } bool danger[maxv]; void init(){ for(int i=0;i<maxv;i++) fa[i]=i; memset(danger,0,sizeof danger); } int T; int n,k; int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ init(); cin>>n>>k; for(int i=0;i<n-1;i++){ int x,y,c; scanf("%d%d%d",&x,&y,&c); es[i]=EDGE(x,y,c); } for(int i=0;i<k;i++){ int x; scanf("%d",&x); danger[x]=1; } sort(es,es+n-1,cmp); ll ans=0; for(int i=0;i<n-1;i++){ int x=es[i].from,y=es[i].to,c=es[i].cost; if(same(x,y)){ ans+=c; }else{ if(danger[find(x)]&&danger[find(y)]) ans+=c; else if(danger[find(x)]||danger[find(y)]){ uni(x,y); danger[find(x)]=1; }else{ uni(x,y); } } } cout<<ans<<endl; } return 0; }
题目:每包垃圾食品里可能有某种卡片,现在要求收集到所有卡片的要买多少包(期望值)。
思路:概率dp,以前做过一道极为相似的题,但是似乎没有记录下来,当时理解也不是很到位结果这题还是没做出来。。。。kuangbin大神说概率dp基本就是期望值逆推,概率正推,确实如此。计算期望值的时候,dp的状态是到终态的期望步数,那么终态的dp值是0,然后对于每一个状态列出概率方程:当前状态(的期望步数)=1+当前状态×当前状态木发生改变的概率+(求和)能够转移到的状态×转移到该状态的概率。然后移项得到转移方程。注意右面的概率相加一定是1,因为要考虑所有转移的情况。(ps:这题还可以用容斥原理做,但是总感觉原理不明。。)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; int n; double p[30]; double dp[1<<20]; int main(){ //////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ while(cin>>n){ memset(dp,0,sizeof dp); double sump=0; for(int i=0;i<n;i++){ scanf("%lf",p+i); sump+=p[i]; } double rem=1-sump; dp[(1<<n)-1]=0; for(int i=(1<<n)-2;i>=0;i--){ dp[i]=1; double dv=rem; for(int j=0;j<n;j++){ if(!(i&(1<<j))){ dp[i]+=dp[i|(1<<j)]*p[j]; }else{ dv+=p[j]; } } dp[i]/=1-dv; } printf("%.10f\n",dp[0]); } return 0; }
26 HDU 4314(dp
题目:有一些人困在了井里,每个人给出身高臂长,问最多有多少人能逃出。
思路:根据身高与臂长之和排序,如果一个集合里的人都能够逃出,那么先让身高臂长和较小的人出去肯定是没问题的(人为先规定一个顺序),那么可以将a+b降序排序,考虑dp[i][j]表示前i个人跑出j个人所需要的最小身高补偿(如果超出了那么为负),状态转移分第i个人出去或者不出两种。这样对应了a+b小者先出,是因为dp在考虑i时已经计算了a+b比i大的所有人的身高补偿情况,所以i出去的情况对应着它踩在别人上面。
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2005; struct dat{ ll a,b,sum; }a[maxv]; bool cmp(dat a,dat b){ if(a.sum!=b.sum) return a.sum>b.sum; else return a.b<b.b; } int N,H; ll dp[maxv][maxv]; ll sum[maxv]; int main(){ ///freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ while(cin>>N){ memset(dp,0x3f,sizeof dp); for(int i=0;i<=N;i++) for(int j=i+1;j<=N;j++) dp[i][j]=(1LL<<62); for(int i=0;i<=N;i++) dp[i][0]=0; for(int i=0;i<N;i++){ int x,y; scanf("%d%d",&x,&y); a[i+1].a=x,a[i+1].b=y,a[i+1].sum=x+y; } cin>>H; sort(a+1,a+N+1,cmp); for(int i=1;i<=N;i++) sum[i]=sum[i-1]+a[i].a; for(int i=1;i<=N;i++){ for(int j=1;j<=i;j++){ dp[i][j]=min(dp[i-1][j]-a[i].a,max(dp[i-1][j-1],H-sum[i-1]-a[i].sum)); } } int ans=0; for(int i=0;i<=N;i++) if(dp[N][i]<=0) ans=i; cout<<ans<<endl; } return 0; }