數據結構java
hdu4339(樹狀數組+二分)node
題意:給出長度分別爲l1和l2的字符串s1和s2,要求進行k次操做,每次操做輸入的第一個數若是是1,接着就輸入兩個整數a,i和一個字符c,要求將第a個字符串的下標爲i的字符換位c。若是輸入的第一個數是2,緊接着就輸入一個數字i,要求找到一個最大的數字j,使得在區間[i,j)中兩個字符串的字符相等。 分析:以前看到題目給了10s直接暴力求解TLE了。。。後來從別人的題解中受到了啓發,須要用到樹狀數組,這樣能節省很多時間開銷。用一個數組保存狀態(若是s1[i]=s2[i]就爲0,不然爲1),每次進行1操做時更新樹狀數組的數據,而進行2操做時用二分找到知足sum(j)-sum(i)=0的最大j就能夠了。這道題又加深了我對樹狀數組的理解。 #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000005; char s1[maxn],s2[maxn]; int c[maxn]; int k,len; int lowbit(int x) { return x&-x; } void update(int i,int x)//更新樹狀數組的數據 { for(;i<=len;i+=lowbit(i)) c[i]+=x; } int sum(int i)//求和操做 { int sum=0; for(;i>0;i-=lowbit(i)) sum+=c[i]; return sum; } int Search(int i)//二分查找 { int left=i,right=len,mid=0,ans=i-1; while(left<=right) { mid=(left+right)/2; int x=sum(mid)-sum(i-1); //cout<<"mid="<<mid<<",sum(mid)="<<sum(mid)<<",sum(i-1)="<<sum(i-1)<<endl; if(x==0) { ans=mid; left=mid+1; } else right=mid-1; } return ans-i+1; } int main() { int T; cin>>T; int cnt=1; while(T--) { memset(c,0,sizeof c); printf("Case %d:\n",cnt++); scanf("%s%s",s1,s2); scanf("%d",&k); int len1=strlen(s1),len2=strlen(s2); len=min(len1,len2); for(int i=0;i<len;i++) { if(s1[i]!=s2[i]) { update(i+1,1); } } /*for(int i=1;i<=len;i++) printf("sum%d=%d ",i,sum(i));*/ while(k--) { int t; scanf("%d",&t); if(t==1) { int a,i; char ch; scanf("%d %d %c",&a,&i,&ch); bool flag=(s1[i]==s2[i]); if(a==1) s1[i]=ch; else s2[i]=ch; if(flag&&s1[i]!=s2[i]) { update(i+1,1); } if((!flag)&&s1[i]==s2[i]) { update(i+1,-1); } } else { int i; scanf("%d",&i); int ans=Search(i+1); printf("%d\n",ans); } } } return 0; }
hdu5737(有序表線段樹)ios
解析:這是我之前沒有接觸過的線段樹類型,有序表線段樹,每一個節點申請了兩段空間,主要是爲了保存左邊兒子會有多少比v小的,右邊兒子會有多少比v小 的,因此在建樹過程當中要歸併排序。可能我講起來比較難懂,詳見代碼,我給了註釋。 代碼 #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> using namespace std; typedef __int64 LL; #define e tree[id] #define lson tree[id*2] #define rson tree[id*2+1] const int mod=1e9+7; const int maxn=100005; const int C=~(1<<31),M=(1<<16)-1; int rnd(int last,int &a,int &b) { a=(36969+(last>>3))*(a&M)+(a>>16); b=(18000+(last>>3))*(b&M)+(b>>16); return (C&((a<<16)+b))%1000000000; } int n,m,A,B,a[maxn],b[maxn],c[maxn],xs[maxn]; bool cmp(const int& x,const int& y){ return b[x]<b[y]; } int data[maxn<<6],*p; int* New(int len){ p+=len; return p-len; } //靜態的申請空間 void init() //離散化 { for(int i=0;i<n;i++) xs[i]=b[i]; sort(xs,xs+n); for(int i=0;i<n;i++) b[i]=upper_bound(xs,xs+n,b[i])-xs-1; for(int i=0;i<n;i++) a[i]=upper_bound(xs,xs+n,a[i])-xs-1; p=data; //在最開始的位置 } struct Tree { int le,ri,d,sum; int *lid,*rid; void Set(int v){ d=v; sum=v+1; } int GoLe(int v){ return v==-1?v:lid[v]; } //左邊有多少<=v的 int GoRi(int v){ return v==-1?v:rid[v]; } //右邊有多少<=v的 }tree[4*maxn]; void pushup(int id){ e.sum=lson.sum+rson.sum; } //取和 void pushdown(int id) { if(e.d!=-2) //延遲更新 { lson.Set(e.GoLe(e.d)); rson.Set(e.GoRi(e.d)); e.d=-2; } } void Build_tree(int id,int le,int ri) { e.le=le,e.ri=ri,e.d=-2; e.lid=New(ri-le+1); //左右都要申請空間 e.rid=New(ri-le+1); if(le==ri){ e.sum=(a[le]>=b[le]); return; } int mid=(le+ri)/2; Build_tree(id*2,le,mid); Build_tree(id*2+1,mid+1,ri); pushup(id); int ll=mid-le+1,rl=ri-mid; int *vl=b+le,*vr=b+mid+1; int i=0,j=0,cnt=0; while(i<ll&&j<rl) //歸併排序 { if(vl[i]<vr[j]) //左邊小於右邊 { e.lid[cnt]=i; e.rid[cnt]=j-1; c[cnt++]=vl[i++]; } else { e.lid[cnt]=i-1; e.rid[cnt]=j; c[cnt++]=vr[j++]; } } while(i<ll) //左邊沒完的 { e.lid[cnt]=i; e.rid[cnt]=j-1; c[cnt++]=vl[i++]; } while(j<rl) //右邊沒完的 { e.lid[cnt]=i-1; e.rid[cnt]=j; c[cnt++]=vr[j++]; } int k=0; for(int i=le;i<=ri;i++) b[i]=c[k++]; } void Update(int id,int x,int y,int v) //更新 { int le=e.le,ri=e.ri; if(x<=le&&ri<=y){ e.Set(v); return; } //在區間內 pushdown(id); int mid=(le+ri)/2; if(x<=mid) Update(id*2,x,y,e.GoLe(v)); //左邊GoLe表明左邊有多少<=v的 if(y>mid) Update(id*2+1,x,y,e.GoRi(v));//右邊同理 pushup(id); } int Query(int id,int x,int y) //查詢 { int le=e.le,ri=e.ri; if(x<=le&&ri<=y) return e.sum; //在區間內直接返回值 pushdown(id); //延遲更新 int mid=(le+ri)/2; int ret=0; if(x<=mid) ret+=Query(id*2,x,y); //加上左邊 if(y>mid) ret+=Query(id*2+1,x,y); //加上右邊 return ret; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&n,&m,&A,&B); for(int i=0;i<n;i++) scanf("%d",&a[i]); //輸入 for(int i=0;i<n;i++) scanf("%d",&b[i]); init(); Build_tree(1,0,n-1); //建樹 int last=0,ret=0; for(int i=1;i<=m;i++) { int l=rnd(last,A,B)%n; int r=rnd(last,A,B)%n; int x=rnd(last,A,B)+1; if(l>r) swap(l,r); if((l+r+x)%2) //爲奇數是插入 { x=upper_bound(xs,xs+n,x)-xs-1; Update(1,l,r,x); } else //不然查詢 { last=Query(1,l,r); ret=(ret+(LL)i*last%mod)%mod; } } printf("%d\n",ret); } return 0; }
hdu5381(莫隊算法)算法
解析: 莫隊,先預處理出以i爲右端點的區間的gcd值,有一些連續的區間的gcd值是相同的,好比[j,i],[j+1,i],[j+2,i]的gcd值是相同的,咱們能夠把[j,j+2]這個 區間保存下來。同時也預處理出以i爲左端點的,最後用莫隊算法。詳見代碼實現。 代碼 #include<cstdio> #include<vector> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; const int maxn=10005; typedef __int64 LL; int N,M,A[maxn],block; struct Ques { int x,y,id; Ques(int x=0,int y=0,int id=0):x(x),y(y),id(id){} bool operator < (const Ques& t) const { int a=x/block,b=t.x/block; //排序 if(a!=b) return a<b; return y<t.y; } }ques[maxn]; int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } struct node { int id,g; node(int id=0,int g=0):id(id),g(g){} }; vector<node> vl[maxn],vr[maxn]; void GetLe() { for(int i=1;i<=N;i++) //獲得以i爲右端點連續區間的gcd值 { if(i==1){ vl[i].push_back(node(i,A[i])); continue; } int g=A[i],id=i; int Size=vl[i-1].size(); for(int j=0;j<Size;j++) { node& t=vl[i-1][j]; int ng=gcd(t.g,g); if(ng!=g) vl[i].push_back(node(id,g)); g=ng; id=t.id; } vl[i].push_back(node(id,g)); } } void GetRi() //同理 { for(int i=N;i>=1;i--) { if(i==N){ vr[i].push_back(node(i,A[i])); continue; } int g=A[i],id=i; int Size=vr[i+1].size(); for(int j=0;j<Size;j++) { node& t=vr[i+1][j]; int ng=gcd(t.g,g); if(ng!=g) vr[i].push_back(node(id,g)); g=ng; id=t.id; } vr[i].push_back(node(id,g)); } } LL WorkLe(int x,int y) { int Size=vl[y].size(); int ny=y; LL ret=0; for(int i=0;i<Size;i++) { node& t=vl[y][i]; if(t.id>=x) { ret+=(LL)t.g*(ny-t.id+1); ny=t.id-1; //跳過去 } else{ ret+=(LL)t.g*(ny-x+1); break; } } return ret; } LL WorkRi(int x,int y) { int nx=x; LL ret=0; int Size=vr[x].size(); for(int i=0;i<Size;i++) { node& t=vr[x][i]; if(t.id<=y) { ret+=(LL)t.g*(t.id-nx+1); nx=t.id+1; } else { ret+=(LL)t.g*(y-nx+1); break; } } return ret; } LL ans[maxn]; void solve() { for(int i=0;i<=N;i++) vl[i].clear(),vr[i].clear(); block=(int)sqrt(N+0.5); //分塊 sort(ques,ques+M); //排序 GetLe(); //獲得左邊連續相同的gcd區間 GetRi(); //獲得右邊連續相同的gcd區間 int x=1,y=0; LL ret=0; for(int i=0;i<M;i++) //莫隊的主要實現部分 { Ques& q=ques[i]; while(y<q.y){ y++; ret+=WorkLe(x,y); } while(y>q.y){ ret-=WorkLe(x,y); y--; } while(x<q.x){ ret-=WorkRi(x,y); x++; } while(x>q.x){ x--; ret+=WorkRi(x,y); } ans[q.id]=ret; //保存答案 } for(int i=0;i<M;i++) printf("%lld\n",ans[i]); //輸出 } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]);//輸入 scanf("%d",&M); int x,y; for(int i=0;i<M;i++) { scanf("%d%d",&x,&y); ques[i]=Ques(x,y,i); //離線保存查詢 } solve(); } return 0; }
KMP算法數組
//參照《算法入門競賽經典訓練指南》 #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<iostream> using namespace std; void GetFail(char *P,int *fail) { int L=strlen(P); fail[0]=fail[1]=0; for(int i=1;i<L;i++) { int j=fail[i]; while(j&&P[i]!=P[j]) j=fail[j]; fail[i+1]=(P[i]==P[j]?j+1:0); } } void KMP(char *T,char *P,int *fail) { int L1=strlen(T); int L2=strlen(P); GetFail(P,fail); int j=0; for(int i=0;i<L1;i++) { while(j&&T[i]!=P[j]) j=fail[j]; if(T[i]==P[j]) j++; if(j==L2) printf("%d\n",i-L2+1); } } int main() { char T[30],P[20]; int fail[30]; while(scanf("%s%s",T,P)!=EOF) { KMP(T,P,fail); } return 0; }
後綴數組模板數據結構
//參照《算法入門競賽經典訓練指南》 #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; const int maxn=1005; int wa[maxn],wb[maxn],wv[maxn],WS[maxn],sa[maxn]; bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void DA(int *r,int n,int m) //模板 { int i,j,p; int *x=wa,*y=wb; for(i=0;i<m;i++) WS[i]=0; for(i=0;i<n;i++) WS[x[i]=r[i]]++; for(i=1;i<m;i++) WS[i]+=WS[i-1]; for(i=n-1;i>=0;i--) sa[--WS[x[i]]]=i; for(p=1,j=1;p<n;j<<=1,m=p) { for(p=0,i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) WS[i]=0; for(i=0;i<n;i++) WS[wv[i]]++; for(i=1;i<m;i++) WS[i]+=WS[i-1]; for(i=n-1;i>=0;i--) sa[--WS[wv[i]]]=y[i]; swap(x,y); for(p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } int a[maxn],b[maxn],rk[maxn],h[maxn]; void GetHeight(int *r,int n) { for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0; for(int i=0;i<n;i++) { if(k) k--; //先減1 int j=sa[rk[i]-1];//排名在前面的 while(r[i+k]==r[j+k]) k++; //相同一直加 h[rk[i]]=k; } } int main() { int N; while(scanf("%d",&N)!=EOF) { for(int i=0;i<N;i++) scanf("%d",&a[i]); a[N]=0; DA(a,N+1,N); GetHeight(a,N); printf("\n"); } return 0; }
ac自動機模板ide
//參照《算法競賽入門經典訓練指南》 #include<cstdio> #include<cstring> #include<string> #include<queue> #include<iostream> #include<algorithm> using namespace std; const int maxn=10005; const int cs=26; struct AC { int next[maxn][cs],val[maxn]; int id; int fail[maxn],last[maxn]; void init(){ memset(next[0],0,sizeof(next[0])); id=0; }//剛開始只初始化根節點 int GetId(char c){ return c-'a'; } void Insert(char *S,int I) //字典樹插入字符串 { int s,be=0; for(int i=0;S[i]!='\0';i++) { s=GetId(S[i]); if(next[be][s]==0) { next[be][s]=++id; //增長新節點 val[id]=0; //置0 memset(next[id],0,sizeof(next[id])); //清空 } be=next[be][s]; } val[be]=I;//保存信息 } void GetFail() //找失配指針 { queue<int> que; fail[0]=0; for(int i=0;i<cs;i++) { int u=next[0][i]; fail[u]=last[u]=0; if(u) que.push(u); } while(!que.empty()) { int x=que.front(); que.pop(); for(int i=0;i<cs;i++) { int u=next[x][i]; if(!u){ next[x][i]=next[fail[x]][i]; continue; } que.push(u); int v=fail[x]; while(v&&!next[v][i]) v=fail[v]; fail[u]=next[v][i]; last[u]=val[fail[u]]?fail[u]:last[fail[u]]; } } } void Print(int i,int j) { if(!j) return; printf("%d: %d\n",i,val[j]); Print(i,last[j]); } void Find(char *T) //匹配 { int L=strlen(T); int j=0,s; for(int i=0;i<L;i++) { s=GetId(T[i]); j=next[j][s]; if(val[j]) Print(i,j); else if(last[j]) Print(i,j); } } }ac; int main() { int T; scanf("%d",&T); while(T--) { ac.init(); int N; scanf("%d",&N); char mom[50],son[10][20]; scanf("%s",mom); for(int i=1;i<=N;i++) { scanf("%s",son[i]); ac.Insert(son[i],i); } ac.GetFail(); ac.Find(mom); } return 0; }
poj3277(掃描線)函數
/* 題意:給出N個矩形,都在同一水平線上,求面積並 解析:裸的掃描線 */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<set> #include<map> #include<stack> #include<queue> #include<sstream> #include<utility> #include<iterator> using namespace std; const int INF=1e9+7; const double eps=1e-7; typedef __int64 LL; const int maxn=80005; int N,num,A[maxn]; struct node { int L,R,H,s; node(int L=0,int R=0,int H=0,int s=0) :L(L),R(R),H(H),s(s){} bool operator < (const node& t) const { return H<t.H; } }Nod[maxn]; struct Tree { int le,ri,lazy; int sum; }tree[4*maxn]; void build_tree(int id,int le,int ri) { Tree& e=tree[id]; e.le=le,e.ri=ri,e.lazy=0,e.sum=0; if(le==ri) return; int mid=(le+ri)/2; build_tree(id*2,le,mid); build_tree(id*2+1,mid+1,ri); return; } void pushup(int id) { Tree& fa=tree[id]; Tree& lson=tree[id*2]; Tree& rson=tree[id*2+1]; if(fa.lazy) fa.sum=A[fa.ri+1]-A[fa.le]; else if(fa.le==fa.ri) fa.sum=0; else fa.sum=lson.sum+rson.sum; } void update(int id,int x,int y,int c) { Tree& e=tree[id]; int le=e.le,ri=e.ri; if(x<=le&&ri<=y) { e.lazy+=c; pushup(id); return; } int mid=(le+ri)/2; if(x<=mid) update(id*2,x,y,c); if(y>mid) update(id*2+1,x,y,c); pushup(id); return; } LL solve() { LL ret=0; sort(Nod+1,Nod+num+1); sort(A+1,A+num+1); int k=1; for(int i=2;i<=num;i++) if(A[i]!=A[k]) A[++k]=A[i]; build_tree(1,1,k); for(int i=1;i<num;i++) { node& t=Nod[i]; int x=lower_bound(A+1,A+k+1,t.L)-A; int y=lower_bound(A+1,A+k+1,t.R)-A-1; update(1,x,y,t.s); ret+=(LL)tree[1].sum*((LL)Nod[i+1].H-Nod[i].H); } return ret; } int main() { while(scanf("%d",&N)!=EOF) { num=0; int a,b,h; for(int i=1;i<=N;i++) { scanf("%d%d%d",&a,&b,&h); Nod[++num]=node(a,b,0,1); A[num]=a; Nod[++num]=node(a,b,h,-1); A[num]=b; } LL ans=solve(); printf("%I64d\n",ans); } return 0; }
poj3225(線段樹區間異或)ui
/* 題意:給出了幾種集合運算,每次操做對一段區間進行其中的一種運算 輸出最後被覆蓋的區間段,要注意開區間與閉區間 解析:注意一下每種運算如何修改區間的值,在線段樹裏再增長兩個變量 d(整個區間的值),c(是否須要異或),具體細節見代碼。 */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<set> #include<map> #include<stack> #include<queue> #include<sstream> #include<utility> #include<iterator> using namespace std; const int INF=1e9+7; const double eps=1e-7; typedef __int64 LL; #define fa tree[id] #define lson tree[id*2] #define rson tree[id*2+1] #define e tree[id] const int maxn=140000; struct Tree { int le,ri,d,c; //d是區間的值,c表明是否須要異或 void UpXor() { if(d!=-1) d^=1; //是整個連續相同的 else c^=1; //懶惰標記異或 } }tree[4*maxn]; void build_tree(int id,int le,int ri) { e.le=le,e.ri=ri,e.d=0,e.c=0; if(le==ri) return; int mid=(le+ri)/2; build_tree(id*2,le,mid); build_tree(id*2+1,mid+1,ri); return; } inline void pushdown(int id) { if(fa.d!=-1) { lson.d=rson.d=fa.d; lson.c=rson.c=0; fa.d=-1; } if(fa.c) { lson.UpXor(); rson.UpXor(); fa.c=0; } } inline void Link(int id,char op) { if(op=='U') fa.d=1,fa.c=0; //並集 else if(op=='D') fa.d=0,fa.c=0; //差集S-T else if(op=='C'||op=='S') fa.UpXor(); //異或 } void update(int id,int x,int y,char op) { int le=e.le,ri=e.ri; if(x<=le&&ri<=y) { Link(id,op); return; } pushdown(id); int mid=(le+ri)/2; if(x<=mid) update(id*2,x,y,op); //左邊可更新 else if(op=='I'||op=='C') lson.d=lson.c=0; //若是是交集或者是差集T-S,左邊都要置爲0 if(y>mid) update(id*2+1,x,y,op); //右邊同理 else if(op=='I'||op=='C') rson.d=rson.c=0; return; } int mark[maxn]; //標記某位置是否被佔 vector<pair<int,int> > ve; void query(int id) { if(e.le==e.ri) { mark[e.le]=e.d; return; } //一直壓到葉子節點 pushdown(id); query(id*2); query(id*2+1); return; } int main() { char op,a,b; int x,y; build_tree(1,0,131070); //擴大了2倍 while(scanf("%c %c%d,%d%c",&op,&a,&x,&y,&b)!=EOF) { getchar(); x*=2; y*=2; //乘2 if(a=='(') x++; //不是實區間加1 if(b==')') y--; if(x>y) //區間爲空 { if(op=='I'||op=='C') tree[1].c=tree[1].d=0; continue; } update(1,x,y,op); //更新 } memset(mark,0,sizeof(mark)); query(1); //整個壓下去 ve.clear(); int pre=-1; for(int i=0;i<=131075;i++) { if(mark[i]) { if(pre!=-1) continue; pre=i; } else { if(pre==-1) continue; ve.push_back(make_pair(pre,i-1)); //找區間段 pre=-1; } } if(ve.size()==0) printf("empty set\n"); //爲空 else { int kase=0; for(int i=0;i<ve.size();i++) //輸出很噁心 { int L=ve[i].first; int R=ve[i].second; if(kase++) printf(" "); if(L%2) printf("("); else printf("["); printf("%d,%d",L/2,(R+1)/2); if(R%2) printf(")"); else printf("]"); } printf("\n"); } return 0; }
codeforces 145E(樹狀數組)spa
給你n個數,實現兩種操做,一種操做是把一段區間的數增長某個值,另外一個操做是查詢一段區間內的數知足"幸運數"要求的總個數,能夠用樹狀數組實現. 由於題目已保證一個數的值不會超過10000,因此咱們能夠先把1~10000的幸運數找出來,或者直接打一個表,這樣查詢一個數是不是幸運數的時間複雜度就是O(1) #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <iostream> #define LL long long using namespace std; int a[100001],c[100002],kk[10002]={0}; int maxn; inline int lowbit(int a) { return a&(-a); } void add(int x,int num) { while(x<=maxn) { c[x]+=num; x+=lowbit(x); } } int getsum(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } bool jug(int a) { while(a) { if(a%10!=4&&a%10!=7) return 0; a/=10; } return 1; } int main() { int i,ans,n,m,b,a1,c1,x1,x2; char s[10]; for(i=1;i<=10001;i++) if(jug(i)) kk[i]=1; while(~scanf("%d %d",&n,&m)) { maxn=n; for(i=0; i<=n+1; i++) c[i]=0; for( i=1; i<=n; i++) { scanf("%d",&a[i]); if(kk[a[i]]==1) add(i,1); } while(m--) { scanf("%s",s); if(s[0]=='c') { scanf("%d %d",&a1,&b); printf("%d\n",getsum(b)-getsum(a1-1)); } else { scanf("%d %d %d",&a1,&b,&c1); for(i=a1; i<=b; i++) { x1=kk[a[i]]; a[i]+=c1; x2=kk[a[i]]; if(x1==1&&x2==0) add(i,-1); else if(x1==0&&x2==1) add(i,1); } } } } return 0; }
Codeforces 276 E(在樹上的操做)
題意: 給你一個無向圖 除了 1號節點之外每一個節點的 度都是 2 即 一個入度一個出度 給你一些操做 輸入0vxd 表示在距離v節點d內的全部節點都增長x 輸入1v 表示查詢v節點的值 關鍵就在於第一個操做的時候往上的節點會延伸到其餘樹鏈上去 這真尼瑪麻煩 博客上給的思路和我一致 維護兩種樹 一個是在原樹鏈上的操做 一個是操做延伸出來的 敲完代碼A掉以後個人心裏真的久久不能平靜 可是仍是有一個地方不大懂 如有大神看到還望指點一下 Code #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> using namespace std; const int maxn=100005; int n,qnum,v,x,d,dis[maxn],root[maxn];//dis表示樹的深度,root表示樹的首節點,以這兩個數組定位一個節點 vector<int> g[maxn];//實現無向圖 vector<vector<long long> > tree;//樹的實現 int lowbit(int x) { return x&(-x); } void add(int st,int en,int val,int cur) { if(st>en) return; int sz=tree[cur].size(); for(int i=st;i<sz;i+=lowbit(i)) tree[cur][i]+=val; for(int i=en+1;i<sz;i+=lowbit(i))//鶸表示這裏不是很理解…………爲何不能一個st~en循環呢………… tree[cur][i]-=val; } long long getsum(int dep,int cur) { long long ret=0; int sz=tree[cur].size(); for (int i=dep; i>0; i-=lowbit(i)) ret+=tree[cur][i]; return ret; } void dfs(int u,int pre,int dep,int cur)//dfs初始化 { dis[u]=dep; root[u]=cur; tree[cur].push_back(0);//每次push進一個0使得size增長,同時初始化每棵樹 int sz=g[u].size(); for (int i=0; i<sz; i++) { int v=g[u][i]; if (v!=pre) dfs(v,u,dep+1,cur); } } int main() { while(scanf("%d%d",&n,&qnum)!=EOF) { int a,b; for(int i=0; i<=n; i++) g[i].clear(); for(int i=0; i<n-1; i++) { scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } int sz=g[1].size(); tree.resize(sz+5); for(int i=0; i<sz; i++) { tree[i+1].push_back(0); dfs(g[1][i],1,1,i+1); } tree[0].resize(maxn,0);//對tree[0]初始化 int op; long long sum=0; while(qnum--) { scanf("%d",&op); if(op) { scanf("%d",&v); int cur=root[v]; if(v==1) printf("%I64d\n",sum); else printf("%I64d\n",getsum(dis[v],cur)+getsum(dis[v],0));//答案是由tree[0]與其原樹相加結果 } else { scanf("%d%d%d",&v,&x,&d); int cur=root[v]; if(d<dis[v]) add(dis[v]-d,dis[v]+d,x,cur);//在一條樹鏈上足以操做 else { sum+=x;//記錄全部add 便於v==1時輸出 add(1,dis[v]+d,x,cur);//在原樹鏈上操做 add(1,d-dis[v],x,0);//上界操做由tree[0]負責 add(1,d-dis[v],-x,cur);//對原樹鏈重複操做還原 } } } } return 0; }
hdu5316(線段樹區間合併)
看一句:A beautiful subsequence is a subsequence that all the adjacent pairs of elves in the sequence have a different parity of position。 意思是說定義美麗子序列是區間裏面的一個子序列,這個子序列中任意相鄰的兩個數在原序列中的具備不一樣的奇偶性 兩種操做,1.把序列x位置的值變成y;2.詢問區間[x,y]裏面的最大美麗子序列(即序列和最大) 區間合併的時候要知足當左子的美麗子序列若是是以奇位置結尾,那麼右子的美麗子序列要以偶開頭 若左邊以偶結尾,右邊則以奇開頭 那麼只須要保存區間內「偶始偶終」、「偶始奇終」、「奇始偶終」、「奇始奇終」四種美麗子序列,合併的時候奇偶配對着合併 用一個二維數組s[2][2]表示起點和終點的奇偶性,其中0表示偶,1表示奇 還有,其中最大的美麗子序列不能夠是空集(意思是說,當最大美麗子序列爲負的時候不要取0 。。。。) 1.#include<iostream> 2.#include<cstdio> 3.#include<algorithm> 4.using namespace std; 5.typedef long long ll; 6.const int N = 100000; 7.const ll inf = 1LL<<30LL; 8.struct Node 9.{ 10. int l,r; 11. ll s[2][2];//0偶1奇 12.}q[4*N]; 13.#define ls i<<1 14.#define rs i<<1|1 15.void pushup(int i) 16.{ 17. for (int j = 0;j <= 1;++j) 18. { 19. for (int k = 0;k <= 1;++k) 20. { 21. q[i].s[j][k] = max(q[ls].s[j][k],q[rs].s[j][k]); 22. q[i].s[j][k] = max(max(q[ls].s[j][0]+q[rs].s[1][k],q[ls].s[j][1]+q[rs].s[0][k]),q[i].s[j][k]); 23. } 24. } 25.} 26.void build(int i,int l,int r) 27.{ 28. q[i].l = l,q[i].r = r; 29. if (l == r) 30. { 31. ll y; 32. scanf("%I64d",&y); 33. for (int j = 0;j <= 1;++j) 34. { 35. for (int k = 0;k <= 1;++k) 36. { 37. q[i].s[j][k] = -inf; 38. } 39. } 40. if (l&1) //奇數 41. { 42. q[i].s[1][1] = y; 43. } 44. else q[i].s[0][0] = y; 45. return ; 46. } 47. int mid = (l+r)>>1; 48. build(ls,l,mid); 49. build(rs,mid+1,r); 50. pushup(i); 51.} 52.void update(int i,int x,ll y) 53.{ 54. if (x == q[i].l &&x == q[i].r) 55. { 56. if (x&1) //奇數 57. { 58. q[i].s[1][1] = y; 59. } 60. else q[i].s[0][0] = y; 61. return; 62. } 63. if (x <= q[ls].r) update(ls,x,y); 64. else update(rs,x,y); 65. pushup(i); 66.} 67.Node query(int i,int l,int r) 68.{ 69. if (l<=q[i].l&&q[i].r<=r) return q[i]; 70. int mid = (q[i].l+q[i].r)>>1; 71. if (r <= mid) return query(ls,l,r); 72. else if (l > mid) return query(rs,l,r); 73. else 74. { 75. Node ql = query(ls,l,mid); 76. Node qr = query(rs,mid+1,r); 77. Node ans; 78. for (int j = 0;j <= 1;++j) 79. { 80. for (int k = 0;k <= 1;++k) 81. { 82. ans.s[j][k] = max(ql.s[j][k],qr.s[j][k]); 83. ans.s[j][k] = max(max(ql.s[j][0]+qr.s[1][k],ql.s[j][1]+qr.s[0][k]),ans.s[j][k]); 84. } 85. } 86. return ans; 87. } 88.} 89.int main() 90.{ 91. int T;scanf("%d",&T); 92. while (T--) 93. { 94. int n,m;scanf("%d %d",&n,&m); 95. build(1,1,n); 96. int op;ll a,b; 97. for (int i = 0;i < m;++i) 98. { 99. scanf("%d",&op); 100. scanf("%I64d %I64d",&a,&b); 101. if (op == 0) 102. { 103. Node ans = query(1,(int)a,(int)b); 104. ll mx = - inf; 105. for (int j = 0;j <= 1;++j) 106. { 107. for (int k = 0;k <= 1;++k) 108. { 109. mx = max(ans.s[j][k],mx); 110. } 111. } 112. printf("%I64d\n",mx); 113. } 114. else if (op == 1) 115. { 116. update(1,(int)a,(ll)b); 117. } 118. } 119. } 120. return 0; 121.}
poj3486(線段樹區間更新)
區間更新和區間查詢有點相似 區間更新是由上往下的 每到一個點 判斷這個點所表明的區間是否是在要更改的區間內 是的話在這個點更改這個點的數值標記 而且把這個點的值在原來值的基礎增減(這個點區間範圍*更改值 )而且返回 (不必定到最底層) 若是這個點自己的區間和他的子區間都不知足目標區間的範圍 直接返回 不然 pushdown 再進入子區間進行判斷 要注意返回後的路徑上的值的更新 由於是自上而下的 因此一直到更新完目標區間 根節點到目標區間路徑上點都被更新了 查詢的時候 也是把查詢路徑上的點一路pushdown 因此最後的答案也是更新過的 1.#include<iostream> 2.#include<algorithm> 3.#include<cstdlib> 4.#include<cctype> 5.#include<cstdio> 6.#include<string> 7.#include<cstring> 8.#include<vector> 9.#include<set> 10.#include<map> 11.#include<queue> 12.#include<cmath> 13.#define pi acos(-1.0) 14.#define inf 1<<29 15.#define INF 0x3f3f3f3f 16.#define zero 1e-8 17. 18.const int li[] = { -1, 0, 1, 0}; 19.const int lj[] = {0, -1, 0, 1}; 20. 21.const int N = 1e5 + 10; 22. 23.using namespace std; 24. 25.struct node { 26. 27. long long data; 28. long long tag; 29. 30.} tree[N * 4]; 31. 32.int arr[N]; 33. 34.void build(int node, int Begin, int End) 35.{ 36. tree[node].tag = 0; 37. if (Begin == End) { 38. tree[node].data = arr[Begin]; 39. return; 40. } 41. build(node * 2, Begin, (Begin + End) / 2); 42. build(node * 2 + 1, (Begin + End) / 2 + 1, End); 43. tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data; 44.} 45.long long fin; 46. 47.void pushdown(int node, int l, int r) 48.{ 49. 50. tree[node * 2].tag += tree[node].tag; 51. tree[node * 2].data += tree[node].tag * ((l + r) / 2 - l + 1); 52. tree[node * 2 + 1].tag += tree[node].tag; 53. tree[node * 2 + 1].data += tree[node].tag * (r - (l + r) / 2); 54. tree[node].tag = 0; 55.} 56. 57.void update(int node, int b, int e, int l, int r, long long data) 58.{ 59. 60. if (l > e || r < b) return; 61. 62. if (l >= b && r <= e) { 63. tree[node].tag += data; 64. tree[node].data += data * (r - l + 1); 65. return; 66. } 67. 68. pushdown(node, l, r); 69. update(node * 2, b, e, l, (l + r) / 2, data); 70. update(node * 2 + 1, b, e, (l + r) / 2 + 1, r, data); 71. 72. tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data; 73.} 74. 75. 76.void query(int node, int b, int e, int l, int r) 77.{ 78. 79. if (l > e || r < b) return; 80. 81. if (l >= b && r <= e) { 82. fin += tree[node].data; 83. return; 84. } 85. pushdown(node, l, r); 86. query(node * 2, b, e, l, (l + r) / 2); 87. query(node * 2 + 1, b, e, (l + r) / 2 + 1, r); 88.} 89.int main() 90.{ 91. 92. int n, q; 93. scanf("%d %d", &n, &q); 94. 95. for (int i = 1; i <= n; ++i) 96. scanf("%d", &arr[i]); 97. 98. build(1, 1, n); 99. 100. for (int i = 0; i < q; ++i) { 101. 102. char ch[10]; 103. scanf("%s", ch); 104. if (ch[0] == 'C') { 105. int a, b, c; 106. scanf("%d%d%d", &a, &b, &c); 107. update(1, a, b, 1, n, c); 108. } else if (ch[0] == 'Q') { 109. int a, b; 110. scanf("%d%d", &a, &b); 111. fin = 0; 112. query(1, a, b, 1, n); 113. printf("%lld\n", fin); 114. } 115. } 116. 117. return 0; 118.}
poj1177(掃描線求周長)
題意: 題目就是說n個矩形組成的新圖形的周長,每一個矩形會給出左下角和右上角的座標。 分析: 線段樹+掃描線+離散化,我是從左到右掃描的,須要將縱座標離散化,而後每掃描到一根線,求出其覆蓋y方向的總長度,減去上次求得的長度,即爲本條線增長或減小的長度,同時能夠求出每兩條線之間的距離,即爲橫座標方向的周長的一部分,因爲是矩形構成的,橫座標方向是爲周長的部分必定成對出現,同時須要用到一個很重要的記錄(num),由於可能出現兩條豎線之間有4,6…甚至更多條外圍的線段。 源代碼: #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<queue> #include<stack> #include<algorithm> using namespace std; const int N=5005; typedef __int64 ll; struct seg { int y1,y2; int x; int flag; seg(){} seg(int _y1,int _y2,int _x,int f):y1(_y1),y2(_y2),x(_x),flag(f){} friend bool operator < (seg a,seg b) { if(a.x==b.x) return a.flag>b.flag; return a.x<b.x; } }Line[N<<1]; struct TNode { int l,r; int num;//子樹中有多少條線段 int cover;//是否被覆蓋,入邊加1,出邊-1 int len;//當前節點覆蓋y方向的總長度 bool lb,rb;//左右節點是否被覆蓋 }tre[N<<3]; vector<int>vec; void build(int o,int l,int r) { tre[o].l=l; tre[o].r=r; tre[o].num=0; if(l==r-1) return; int mid=(l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid,r); } void update_len(int o) { if(tre[o].cover>0) { tre[o].len=vec[tre[o].r]-vec[tre[o].l]; tre[o].lb=tre[o].rb=true; tre[o].num=1; return; } if(tre[o].l==tre[o].r-1) { tre[o].cover=tre[o].lb=tre[o].rb=0; tre[o].num=tre[o].len=0; return; } tre[o].lb=tre[o<<1].lb; tre[o].rb=tre[o<<1|1].rb; tre[o].len=tre[o<<1].len+tre[o<<1|1].len; tre[o].num=tre[o<<1].num+tre[o<<1|1].num-(tre[o<<1].rb&tre[o<<1|1].lb); } void update(int o,seg p) { if(p.y1<=vec[tre[o].l]&&p.y2>=vec[tre[o].r]) { tre[o].cover+=p.flag; update_len(o); return; } if(tre[o].l==tre[o].r-1) return; int mid=(tre[o].l+tre[o].r)/2; if(p.y1<=vec[mid]) update(o<<1,p); if(p.y2>vec[mid]) update(o<<1|1,p); update_len(o); } int main() { int n; while(~scanf("%d",&n)) { int k=0; for(int i=0;i<n;i++) { int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); Line[k++]=seg(y1,y2,x1,1);//進 //y[k]=y1; Line[k++]=seg(y1,y2,x2,-1);//出 //y[k]=y2; vec.push_back(y1); vec.push_back(y2); } sort(vec.begin(),vec.end()); vec.erase(unique(vec.begin(),vec.end()),vec.end()); sort(Line,Line+k); build(1,0,vec.size()-1); //printf("1111\n"); int ans=0; int old=0; int lines=0; for(int i=0;i<k;i++) { //printf("%d\n",Line[i].x); update(1,Line[i]); if(i>=1) { ans+=lines*2*(Line[i].x-Line[i-1].x); } ans+=abs(tre[1].len-old); old=tre[1].len; lines=tre[1].num; } printf("%d\n",ans); } return 0; }
搜索
hdu3812(深搜+剪枝)
題意: 給出N對字符串,每對字符串能夠相連,問可否找到一條從sea到sky的字符串鏈,每一個字符串只能出現一次,若是能,輸出 長度最長的,若是有多解,輸出字典序最小的。無解輸出what a pity 解析: 剛開始還覺得是個有環找最長路的圖論題(然而我並不會寫)。。。。。。看了別人題解才知道是深搜+剪枝。 對字符串排序標號,若是沒有出現sea或者sky,直接無解,而後並查集一下,若是不在同一個集合,一樣無解,而後判斷每一個點 (起點和終點除外)是否能到達起點和終點,不能的在搜的過程當中直接無論,而後就是搜了,搜的過程當中更新解,若是最大長度 已經用完了全部的字符串就不必再搜了(最開始我沒管這個超時了。。。。。。後來加了這個剪枝就過了) 代碼 #include<cstdio> #include<cstring> #include<string> #include<map> #include<vector> #include<algorithm> #include<iostream> using namespace std; const int maxn=202; int N,id,be,en; string S1[maxn],S[maxn]; int d[maxn]; int root(int a){ return d[a]==a?a:d[a]=root(d[a]); } bool G[maxn][maxn]; int GetId(string& s) //找到下標 { for(int i=1;i<=id;i++) if(S[i]==s) return i; return 0; } bool input() { N*=2; for(int i=1;i<=N;i++) { cin>>S1[i]; S[i]=S1[i]; } sort(S+1,S+N+1); //排個序 be=en=0; //起點和終點編號 id=1; for(int i=2;i<=N;i++) { if(S[i]!=S[id]) S[++id]=S[i]; //去重 if(S[id]=="sea") be=id; if(S[id]=="sky") en=id; } if(!be||!en) return false; //沒有sea或者sky for(int i=0;i<maxn;i++) d[i]=i; //並查集 memset(G,false,sizeof(G)); for(int i=1;i<N;i+=2) { int a=GetId(S1[i]); //下標 int b=GetId(S1[i+1]); int ra=root(a); int rb=root(b); G[a][b]=G[b][a]=true; //可連 if(ra!=rb) d[rb]=ra; //合併 } if(root(be)!=root(en)) return false; //不在同一集合 return true; } bool vis[maxn],tvis[maxn]; int maxl,temp[maxn],ans[maxn]; bool dfs(int x,int step) { vis[x]=true; temp[step]=x; if(x==en) //到終點 { if(step>maxl) //更新解 { maxl=step; for(int i=0;i<=maxl;i++) ans[i]=temp[i]; } if(step==id-1) return true; //用完全部的 } for(int i=1;i<=id;i++) if(!vis[i]&&G[x][i]) { if(dfs(i,step+1)) return true; } vis[x]=false; return false; } bool Reach(int x,int y) { if(x==y) return true; for(int i=1;i<=id;i++) if(!tvis[i]&&!vis[i]&&G[x][i]) { tvis[i]=true; if(Reach(i,y)) return true; } return false; } void solve() { memset(vis,false,sizeof(vis)); for(int i=1;i<=id;i++) { memset(tvis,false,sizeof(tvis)); tvis[be]=true; if(!Reach(i,en)) vis[i]=true; //可否到終點 } for(int i=1;i<=id;i++) { memset(tvis,false,sizeof(tvis)); tvis[en]=true; if(!Reach(i,be)) vis[i]=true; //可否到起點 } maxl=0; //最大長度 dfs(be,0); //深搜 } int main() { int T,Case=0; cin>>T; while(T--) { cin>>N; if(!input()) //無解 { printf("Case %d: what a pity\n",++Case); continue; } solve(); printf("Case %d:",++Case); for(int i=0;i<=maxl;i++) cout<<" "<<S[ans[i]]; cout<<endl; } return 0; }
hdu1401(雙向bfs)
題意:棋盤大小爲8*8,而後有4個球,給出初始全部球的位置以及目標位置,每次能夠移動一個球,要麼移動到旁邊空的地方,要麼跨過一個 球到空的地方(不能跨過多個球),問可否在8步之內到達目標狀態。 解析:限定了8步,並且先後搜效果是同樣的,因此能夠考慮用雙向bfs,而後8個數哈希(也能夠開多維數組保存),要注意對4個點要排序。 代碼 #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int mod=500007; const int maxn=200005; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; bool in(int x,int y){ return x>=1&&x<=8&&y>=1&&y<=8; } struct point //球的座標 { int x,y; point(int x=0,int y=0):x(x),y(y){} bool operator < (const point& t) const { if(x!=t.x) return x<t.x; return y<t.y; } }; struct node { point p[4]; //每一個節點保存4個球的座標,而且是排好序的 }nod[2][maxn]; //開二維用於雙向搜 struct Hash { int v,k,nid,next; //保存哈希值,0或1,下標,next指針 }ha[mod+maxn]; int f[2],r[2],hash_id; //隊首隊尾指針 bool Same(int k1,int a,int k2,int b) //判斷兩個結構體是否徹底相同 { for(int i=0;i<4;i++) { if(nod[k1][a].p[i].x!=nod[k2][b].p[i].x) return false; if(nod[k1][a].p[i].y!=nod[k2][b].p[i].y) return false; } return true; } int GetHash(point p[]) //獲得哈希值 { int ret=0,k=1; for(int i=0;i<4;i++) { ret+=p[i].x*k; k*=3; //k是係數 ret+=p[i].y*k; k*=3; } return ret; } int Insert_Hash(int v,int k,int nid) //插入 { int a=v%mod; int p=ha[a].next; while(p!=-1) { if(ha[p].v==v&&Same(ha[p].k,ha[p].nid,k,nid)) return ha[p].k==k?0:1; //有相同的狀態,k值相同返回0,不一樣返回1 p=ha[p].next; } p=++hash_id; ha[p].v=v; ha[p].k=k; ha[p].nid=nid; //增長新的節點 ha[p].next=ha[a].next; ha[a].next=p; return -1; } bool Has(node& t,int x,int y) { for(int i=0;i<4;i++) if(t.p[i].x==x&&t.p[i].y==y) return true; //此處是球 return false; } bool AddNode(node& t,int i,int j,int k) { int x=t.p[i].x; int y=t.p[i].y; int nx=x+dx[j]; int ny=y+dy[j]; if(!in(nx,ny)) return false; //出界 if(Has(t,nx,ny)) nx+=dx[j],ny+=dy[j]; //有球 if(!in(nx,ny)) return false; //出界 if(Has(t,nx,ny)) return false; //還有球 node& tt=nod[k][r[k]]; tt=t; tt.p[i].x=nx; tt.p[i].y=ny; sort(tt.p,tt.p+4); //排序 int a=Insert_Hash(GetHash(tt.p),k,r[k]); if(a==1) return true; //找到解 else if(a==-1) r[k]++; //增長新節點 return false; } bool bfs(int k) { int en=r[k]; while(f[k]<en) { node& t=nod[k][f[k]++]; for(int i=0;i<4;i++) //4個球4個方向 for(int j=0;j<4;j++) if(AddNode(t,i,j,k)) return true; } return false; } bool solve() { if(Same(0,0,1,0)) return true; //相同 int step=0; f[0]=f[1]=0; r[0]=r[1]=1; for(int i=0;i<mod;i++) ha[i].next=-1; hash_id=mod-1; Insert_Hash(GetHash(nod[0][0].p),0,0); Insert_Hash(GetHash(nod[1][0].p),1,0); while(f[0]<r[0]||f[1]<r[1]) { if(step>=4) return false; step++; if(bfs(0)) return true; if(bfs(1)) return true; } return false; } int main() { int x,y; while(scanf("%d%d",&x,&y)!=EOF) { nod[0][0].p[0]=point(x,y); for(int i=1;i<4;i++) { scanf("%d%d",&x,&y); nod[0][0].p[i]=point(x,y); } for(int i=0;i<4;i++) { scanf("%d%d",&x,&y); nod[1][0].p[i]=point(x,y); } sort(nod[0][0].p,nod[0][0].p+4); sort(nod[1][0].p,nod[1][0].p+4); if(solve()) printf("YES\n"); else printf("NO\n"); } return 0; }
模擬貪心亂搞yy
hdu3640(植物大戰殭屍那題,模擬+二分)
題意:模擬植物大戰殭屍,地圖只有一行,有兩種植物:豌豆射手(P)和炸彈土豆(M),殭屍要進攻,每隻殭屍的hp是50,每一個豌豆射手的hp是10, 殭屍若是走在土豆上,土豆會爆炸,全部在土豆處的殭屍hp都變爲0,若是殭屍走在豌豆射手處,則每隻殭屍對豌豆射手攻擊一次,形成1點的傷害, 豌豆射手會攻擊走在最前面的殭屍,每一個豌豆射手攻擊一次,形成1點的傷害。問最少須要多少隻殭屍才能贏。 解析:很噁心的模擬,要注意的是殭屍不是一個接着一個排成隊放在右邊,能夠幾個殭屍放在同一個位置開始進攻,否則若是一開始就有超過50個 豌豆射手,那豈不是進來一個死一個(我最開始覺得是一個一個放,覺得不會有這種無解的狀況。。。。。。),若是殭屍碰到的是炸彈,那麼下一 回合能夠認爲殭屍的最左位置爲炸彈的左邊。若是碰到的不是炸彈,則不停向左統計植物個數,直到遇到第一個炸彈,而後二分能夠吃掉這些植物 的最少殭屍個數。詳見代碼實現。 代碼 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int maxn=105; char S[maxn]; int L,Pnum,Mnum,step; void init() { L=strlen(S)-1; Pnum=Mnum=0; //豌豆射手與土豆的個數 for(int i=0;i<=L;i++) if(S[i]=='P') Pnum++; else Mnum++; } bool judge(int totpnum,int pnum,int znum) { int nowstep=step; int P_hp=10,Z_hp=50; //豌豆射手和殭屍的hp while(pnum>0&&znum>0) { if(nowstep>0){ nowstep--; Z_hp-=totpnum; } //沒走到豌豆射手處 else{ P_hp-=znum; Z_hp-=totpnum; } //走到了兩個都要受傷害 if(P_hp<=0) //死一個射手 { P_hp=10; totpnum--; pnum--; nowstep=1; } if(Z_hp<=0){ Z_hp=50; znum--; } //死一個殭屍 } if(pnum<=0&&znum>0) return true; //全部豌豆射手掛掉而殭屍有多的 return false; } int BIS(int totpnum,int pnum) //二分 { int x=1,y=2*maxn,mid; while(x<=y) { mid=(x+y)/2; if(judge(totpnum,pnum,mid)) y=mid-1; else x=mid+1; } return y+1; } int solve() { int ret=0; //ret是須要的殭屍的個數 if(S[L]=='M') { ret++; L--; step=2; } //最右邊是土豆,step表明走到下一個位置須要的步數 else step=1; while(L>=0) //知道小於0纔算贏 { if(S[L]=='M') //是土豆 { if(step>1) //往前走 { step--; if(Pnum>=50) ret++; //超過50個豌豆射手死一個殭屍 continue; } else ret++; //炸死一個 Mnum--; L--; step=2; //step變爲2 } else //若是是豌豆射手 { int pnum=0; for(int i=L;i>=0;i--) //一直到找到土豆 if(S[i]=='M') break; else pnum++; ret+=BIS(Pnum,pnum); //二分獲得最小的殭屍個數 Pnum-=pnum; L-=pnum+1; //多減1表示把土豆算進去了,由於有沒死的殭屍在土豆處犧牲 step=2; } } if(S[0]=='M') ret++; //若是最左邊是土豆,則還須要一個殭屍去攻擊首腦 return ret; } int main() { int T,Case=0; scanf("%d",&T); while(T--) { scanf("%s",S); //植物 init(); printf("Case %d: %d\n",++Case,solve()); } return 0; }
hdu3866(貪心)
題意:n人合夥買一件價格爲p的禮物,每一個人有本身能承擔的最大費用,要保證儘量公平,使付錢多的人和付錢少的人錢數的差距儘量小,若是存在矛盾,先來的人比後來的付錢多。 分析:要使盡量公平,能夠先將總價格平分,若是有人能承擔的最大費用比平均價格要少,則要支付本身所有的錢,而後剩餘的錢由其它人循環按照此法解決,當最終剩餘的人能承擔的最大費用都比當前平均費用多時,則按照題目規則需多付錢的人比其餘人少付1cent便可徹底解決問題。可見該題是一道貪心問題,可建立結點數組a用於保存每人能承擔的最大費用s和本身的序號x,用數組b按序號保存沒人須要支付的費用。 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=10005; int p,n; bool vis[maxn]; //標記數組,若某結點已訪問,則將其按序號標記爲true struct node { int s,x; }a[maxn]; int b[maxn]; bool cmp(const node &a,const node &b) //將結點數組按s從小到大排序,若s相同則按x從小到大排序 { if(a.s<b.s) return true; else if(a.s==b.s) return a.x>b.x; return false; } int main() { int T; cin>>T; while(T--) { memset(vis,false,sizeof vis); scanf("%d%d",&p,&n); ll sum=0; for(int i=0;i<n;i++) { scanf("%d",&a[i].s); a[i].x=i; sum+=a[i].s; } if(sum<p) { puts("IMPOSSIBLE"); continue; } int nn=n; while(p) { sort(a,a+n,cmp); int ave=p/nn,mod=p%nn; int num=0; for(int i=0;i<n;i++) { if(vis[a[i].x]) continue; if(a[i].s<=ave) { b[a[i].x]=a[i].s; p-=a[i].s; vis[a[i].x]=true; num++; nn--; } else break; } if(num==0) { for(int i=n-1;i>=0;i--) { if(vis[a[i].x]) continue; if(mod) { b[a[i].x]=ave+1; mod--; } else b[a[i].x]=ave; } break; } } for(int i=0;i<n-1;i++) printf("%d ",b[i]); printf("%d\n",b[n-1]); } return 0; }
codeforces 239 div2 D
基本題意:總共有n+1個房間,每一個房間有兩個門,在這裏我假設爲A門和B門,這兩個門其中第一個只能回到前邊幾個房間,假設該房間爲第i個房間,那麼他只能回到對應的前邊的1到i號房間(ps這裏爲何會有i號房間呢,後邊一句來解釋),具體是多少,得看輸入的時候對應的是多少(也就是說若是你輸入的就是i,那麼也就是表明又再次回到原來的房間,回到同一個房間),第二道門是隻可以到達下邊一個房間,假設如今就在第i個房間,那麼只能前往第i+1號房間,我在這裏假設這兩個房間爲A門和 B門,A門往前邊走,B門日後邊走。並且一走進一個房間以後 ,這個房間就會有個計數器,這個計數器是隻要你進去就會記一次數,若是計數以後爲奇數(odd number),那麼就只能走A門出去,即回到前邊幾個房間 數學公式的考察:這道題主要注意有個循環的過程,每次從一個門裏邊出來(若是你再次回到同一個房間,那麼這個房間的計數器會變成奇數,(由於一開始是從這個房間的而A門出去的(ps由於你是經過回到同一個房間的,也就是說你是從A門走到1到i號房間的纔到這歌兒同一個房間),那麼也就是說那個時候計數器就是奇數,那麼當你一進來那麼就變成偶數了)這個時候就會走B門,到達那個i+1號房間)這就得出一個結論,就是隻要你第一次走進一個房間(這個房間計數器變爲奇數1),那麼你必定是先走A門出去,前往前邊幾個房間(包括本身的房間),而後就再回來這個房間(這個房間計數器變成2(偶數)),而後就走B們出去走到i+1號房間最後要求你到達i+1號房間總共花多少步驟。上邊說的都是理解題意得思路; 下邊來講解題思路:記s[i]爲在第i號房間從B門出來前往i+1號門的時候所已經用過的步子數。所求的s[i]就是從i號房間的A們出去再回到這個房間所需的步子數加上回到這個房間以後從B門出去前往i+1號房間的步子數(固然這裏包括了到達第i-1號房間已經走過的步子數)。最後求得就是s[n];s[i]=2*s[i-1]-s[a[i]-1]+2;這個就是遞推關係式,下邊簡單解釋一下,這個柿子變下心就是s[i]=(s[i-1]+1)+(s[i-1]-s[a[i]-1]+1),前邊一個就是從第i-1號房間直接走到i號房間,而後直接走B門到i+1號房間,中間沒有算A號門,即沒有考慮從i號門回到前邊的第a[i]號門,你理解一下下,後邊一個柿子就是從第i號門經過走A號門回到第a[i]號門,而後再回來的所花的步子數,在這裏有個小技巧,從第i號房間回到第a[i]號房間其實就等價於從a[i]-1號房間到達a[i]號房間的,也就是s[a[i]-1],這裏讀者好好細想一下,應該能夠想明白的。 #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <iostream> using namespace std; int n; int a[1005]; long long s[1005]; int main() { while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) scanf("%d",&a[i]); memset(s,0,sizeof s); s[0]=0; for(int i=1;i<=n;i++) s[i]=(2*s[i-1]%1000000007-s[a[i]-1]%1000000007+1000000009)%1000000007; printf("%I64d\n",s[n]%1000000007); } return 0; }
hdu1112(模擬)
模擬 注意鑰匙的左右邊緣不能超過鎖的範圍 就算是空白也不行 1.#include<iostream> 2.#include<algorithm> 3.#include<cstdlib> 4.#include<cctype> 5.#include<cstdio> 6.#include<string> 7.#include<cstring> 8.#include<vector> 9.#include<queue> 10.#include<cmath> 11.#define pi acos(-1.0) 12.#define inf 1<<29 13.#define INF 0x3f3f3f3f 14.#define zero 1e-8 15. 16.using namespace std; 17. 18.char lock[10507][1207]; 19.char key[127][127]; 20.bool flag[1107][1207]; 21. 22.struct edge { 23. int x, y; 24.} lef[10007], rig[10007], down[10007]; 25. 26.int l, rr, d; 27.int r, c, n, m; 28.int depth; 29.int len; 30.void todo() 31.{ 32. l = 0; 33. r = 0; 34. d = 0; 35. len = 0; 36. 37. for (int i = 0; i < rr; ++i) 38. for (int j = 0; j < c; ++j) { 39. if (key[i][j] == '#') { 40. if (j > 0 && key[i][j - 1] == '#') continue; 41. lef[l].x = i; 42. lef[l++].y = j; 43. 44. } 45. } 46. for (int i = 0; i < rr; ++i) 47. for (int j = c - 1; j >= 0; --j) { 48. if (key[i][j] == '#') { 49. if (j < c - 1 && key[i][j + 1] == '#') continue; 50. rig[r].x = i; 51. rig[r++].y = j; 52. } 53. } 54. 55. for (int j = 0; j < c; ++j) 56. for (int i = rr - 1; i > -1; --i) { 57. if (key[i][j] == '#') { 58. if (i < rr - 1 && key[i + 1][j] == '#') continue; 59. down[d].x = i; 60. down[d++].y = j; 61. } 62. } 63. 64.} 65. 66.bool j_left(int x, int y) 67.{ 68. for (int i = 0; i < l; ++i) 69. if (y < 0 || lock[lef[i].x + x][lef[i].y + y] == '#') { 70. return false; 71. } 72. 73. return true; 74.} 75. 76.bool j_right(int x, int y) 77.{ 78. 79. for (int i = 0; i < r; ++i) { 80. if (c + y > m || lock[rig[i].x + x][rig[i].y + y] == '#') { 81. return false; 82. } 83. 84. } 85. 86. return true; 87.} 88. 89.bool j_down(int x, int y) 90.{ 91. 92. 93. for (int i = 0; i < d; ++i) { 94. 95. if (x > n + rr || lock[down[i].x + x][down[i].y + y] == '#') { 96. return false; 97. } 98. } 99. return true; 100.} 101. 102.void rem(int x, int y) 103.{ 104. 105. flag[down[0].x + x][down[0].y + y] = true; 106.} 107. 108.bool ifdo(int x, int y) 109.{ 110. 111. if (!flag[down[0].x + x][down[0].y + y]) return true; 112. return false; 113. 114.} 115.void dfs(int x, int y) 116.{ 117. 118. if (x > depth) depth = x; 119. if (depth == n + rr) return ; 120. rem(x, y); 121. 122. if (j_down(x + 1, y) && ifdo(x + 1, y)) dfs(x + 1, y); 123. if (j_left(x, y - 1) && ifdo(x, y - 1)) dfs(x, y - 1); 124. if (j_right(x, y + 1) && ifdo(x, y + 1)) dfs(x, y + 1); 125. 126.} 127. 128. 129.int main() 130.{ 131. 132. int t; 133. cin >> t; 134. for (; t--;) { 135. 136. scanf("%d %d", &rr, &c); 137. 138. for (int i = 0; i < rr; ++i) { 139. scanf("%s", key[i]); 140. } 141. 142. scanf("%d %d", &n, &m); 143. 144. memset(lock, '.', sizeof(lock)); 145. 146. for (int i = rr; i < rr + n; ++i) { 147. scanf("%s", lock[i]); 148. } 149. 150. 151. todo(); 152. depth = 0; 153. if (c > m) { 154. printf("The key falls to depth 0.\n"); 155. continue; 156. } 157. if (r == 0 && l == 0 && d == 0) { 158. printf("The key can fall through.\n"); 159. continue; 160. } 161. 162. memset(flag, false, sizeof(flag)); 163. 164. dfs(0, 0); 165. if (depth >= n + rr) printf("The key can fall through.\n"); 166. else 167. printf("The key falls to depth %d.\n", depth); 168. } 169. 170. return 0; 171.}
HDU5301
題意: 給axb大小的矩陣求出 不覆蓋X點的能填滿這個矩陣的 最大的矩陣的最小值 由於沒有給n m誰大誰小, 先處理了,保證n是短邊,m是長邊.這樣方便計算.(若是要改變的時候,記得將x,y也交換) 先無論X點,計算均分這個矩形的最小矩形的長度,就是最短邊/2往上取整的那個數. 有特殊狀況 若是 m==n && n是奇數 && x==y && x==(n+1)/2 這種狀況 n是偶數的話 不會出現這樣. 正常的話 是這樣 (例 n=7 m=19 x=1 y=5) n!=m 時 就判斷 x y的位置 來判斷這個矩形的長度 並且 由於要最小 因此短邊一直爲1 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> #include<vector> using namespace std; int main() { int n,m,x,y; while (~scanf("%d%d%d%d",&n,&m,&x,&y)) { if (n>m) { swap(n,m); swap(x,y); } int ans=n/2; int half=n-ans; ans=max(ans,half); if (n==m && n%2 && x==y && x==(n+1)/2) ans--; else if (n!=m) { int h=max(x-1,n-x); int w=min(y-1,m-y)+1; if (w>ans) ans=max(ans,min(h,w)); } printf("%d\n",ans); } return 0; }
圖論
hdu1384(差分約束)
題意:對於每一個區間[a,b]至少要有c個元素,問集合裏元素的最小個數。 解析:差分約束,用Ti表示區間[0,i-1]有多少個元素在裏面,則知足下面的條件 Tb+1-Ta>=c 0<=Ti+1-Ti<=1 則建邊(a,b+1,c),(i,i+1,0),(i+1,i,-1) 而後用spfa求得答案。注意這題用vector可能會超時。 代碼 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int INF=1e9+7; const double eps=1e-7; const int maxn=50005; int N; struct edge { int u,v,w,next; edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){next=-1; } }E[maxn*3]; int head[maxn],dist[maxn]; bool inq[maxn]; queue<int> que; int spfa(int be,int en) { for(int i=be;i<=en;i++) dist[i]=-INF; dist[be]=0; memset(inq,false,sizeof(inq)); while(!que.empty()) que.pop(); que.push(be); while(!que.empty()) { int u=que.front(); que.pop(); inq[u]=false; for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v,w=E[i].w; if(dist[v]<dist[u]+w) //更新 { dist[v]=dist[u]+w; if(!inq[v]){ inq[v]=true; que.push(v); } } } } return dist[en]; } int main() { while(scanf("%d",&N)!=EOF) { memset(head,-1,sizeof(head)); int u,v,w,cnt=0; int minv=INF,maxv=-INF; for(int i=0;i<N;i++) { scanf("%d%d%d",&u,&v,&w); //建邊(u,v+1,w); v++; E[++cnt]=edge(u,v,w); E[cnt].next=head[u]; head[u]=cnt; minv=min(minv,u); maxv=max(maxv,v); } for(int i=minv;i<maxv;i++) { E[++cnt]=edge(i,i+1,0); //建邊(i,i+1,0) E[cnt].next=head[i]; head[i]=cnt; E[++cnt]=edge(i+1,i,-1); //建邊(i+1,i,-1) E[cnt].next=head[i+1]; head[i+1]=cnt; } printf("%d\n",spfa(minv,maxv)); } return 0; }
codeforces 14D
題意: 給你一個無向不成環的圖 讓你找出兩條不相交的樹鏈 使其乘積最大 思路 枚舉每一條道路 並使其暫時斷裂 再從這條道路的兩端開始搜索 尋找最長的樹鏈便可 說實話 搜索的題目雖說作的還湊合 可是這種給你一個連通方式 並以此造成無向圖 再加以搜索的題目我仍是第一次作 我先想得是用二維矩陣保存路徑 以G[u][v] 表示從 u到v是連通的 再用vector省點空間 以後我較爲習慣的用了BFS 而後就進入了無限懵逼崩潰懵逼崩潰的狀態 說到底用矩陣保存若是不用上結構體的話 很難記錄鏈長 無奈仍是寫了DFS #include <iostream> #include <cstdio> #include <vector> #include <algorithm> #include <cmath> #include <cstring> #include <queue> using namespace std; const int maxn=220; bool vis[maxn]; vector<int> G[maxn]; int ans; int dfs(int v,int x) { int sum=0; int max1=0,max2=0; for(int i=0; i<G[v].size(); i++) { if(G[v][i]!=x) { sum=max(sum,dfs(G[v][i],v));//sum獲得從一個NODE出發的樹的直徑 循環操做獲得最大 //與此同時 ans也在計算着從改點出發的最長樹鏈 if(ans>max1) { max2=max1; max1=ans; } else max2=ans>max2?ans:max2; } } sum=max(sum,max1+max2);//最後sum獲得從V出發的樹的直徑 ans=max1+1;//max1爲該node的最長樹鏈 由於遞歸返回 最大樹鏈長度+1 return sum; } int main() { int n,a,b; while(scanf("%d",&n)!=EOF) { for(int i=0; i<=n; i++) G[i].clear(); for(int i=0; i<n-1; i++) { scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } int last_ans=0; for(int i=0; i<n; i++) { int num=G[i].size(),temp; for(int j=0; j<num; j++) { int ans=0; temp=dfs(i,G[i][j])*dfs(G[i][j],i); last_ans=max(last_ans,temp); } } printf("%d\n",last_ans); } return 0; }
hdu5294(最短路+最大流)
題意: n個點,m條邊,兩我的,A在點1處,B在點n處,A必須走最短路到達n點(只走最短路,最短路可能多條) B切斷圖中的某些邊不讓A到達n點 問:1.B最少切斷多少邊使A不能到達n點 2.B最多切斷多少邊使A還能達到n點 分析: 問題1求最小割,根據最大流最小割定理,利用最大流求得,流量是1(邊數) 對於問題2,只需在全部的最短路中找到邊數最少的,而後用總邊數m減去最少邊的最短路上的邊數 解法: 以給出的邊權求最短路,根據最短路建圖,判斷邊是否在最短路里面:d[v] - d[u] == w(u,v) 根據所建的新圖,再求最短路(求最短路中的最少邊數),每條邊的權值變爲1, 根據新圖求最大流,流量爲1(即最小割裏的邊權是1,是邊數) 說明:代碼很長不過都是模板,最大流的bfs、dfs模板均可以,最短路用的SPFA模板(很奇怪用Dijkstra怎麼都過不了,不是超時而是WA。。。) 代碼:dfs版增廣路: 1.#define mem(a,x) memset(a,x,sizeof(a)) 2.#include<iostream> 3.#include<cstdio> 4.#include<cstring> 5.#include<algorithm> 6.#include<queue> 7.#include<set> 8.#include<stack> 9.#include<cmath> 10.#include<map> 11.#include<stdlib.h> 12.#include<cctype> 13.#include<string> 14.using namespace std; 15.typedef long long ll; 16.const int N = 2000; 17.const int inf = 1 << 27; 18.const int M = 120000; 19./****************************************最大流模板*********************************************/ 20.struct Edge 21.{ 22. int to;//終點 23. int cap;//容量 24. int rev;//反向邊 25. Edge (int to = 0, int cap = 0, int rev = 0) : to (to), cap (cap), rev (rev) {} 26.}; 27.struct EdmondsKarp 28.{ 29. bool used[M + 5]; 30. vector<Edge>v[M + 5]; 31. void AddEdge (int from, int to, int cap) 32. { 33. v[from].push_back (Edge (to, cap, v[to].size() ) ); 34. v[to].push_back (Edge (from, 0, v[from].size() - 1) ); 35. } 36. int dfs (int s, int t, int f) 37. { 38. if (s == t) return f; 39. used[s] = 1; 40. for (int i = 0; i < v[s].size(); ++i) 41. { 42. Edge &tmp = v[s][i];//引用 43. if (!used[tmp.to] && tmp.cap > 0) 44. { 45. int d = dfs (tmp.to, t, min (f, tmp.cap) ); 46. if (d > 0) 47. { 48. tmp.cap -= d; 49. v[tmp.to][tmp.rev].cap += d; 50. return d; 51. } 52. } 53. } 54. return 0; 55. } 56. int MaxFlow (int s, int t) //求源點匯點的最大流 57. { 58. int flow = 0; 59. while (1) //一直循環直到找不到增廣路 60. { 61. mem (used, 0); 62. int f = dfs (s, t, inf); 63. if (f == 0) return flow; 64. flow += f; 65. } 66. } 67.} q; 68./***************************************最短路模板*****************************************/ 69.struct Node 70.{ 71. int v, w; 72. Node (int v = 0,int w = 0):v(v),w(w){} 73.}; 74.struct Spfa 75.{ 76. int d[N+5]; 77. bool vis[N+5]; 78. queue<int>q; 79. vector<Node>u[N+5]; 80. void init(int n) 81. { 82. while (!q.empty()) q.pop(); 83. for (int i = 0;i <= n;++i) u[i].clear(); 84. mem(vis,0); 85. } 86. void AddEdge(int uu,int vv,int ww) 87. { 88. u[uu].push_back(Node(vv,ww)); 89. u[vv].push_back(Node(uu,ww)); 90. } 91. int spfa(int s, int t,int n) 92. { 93. for (int i = 0; i <= n; ++i) d[i] = inf; 94. q.push (s); 95. vis[s] = 1; 96. d[s] = 0; 97. while (!q.empty() ) 98. { 99. int h = q.front(); 100. q.pop(); 101. vis[h] = 0;//spfa不一樣於bfs 102. for (int i = 0; i < u[h].size(); ++i) 103. { 104. int &v = u[h][i].v, &w = u[h][i].w; 105. if (d[v] > d[h] + w) 106. { 107. d[v] = d[h] + w; 108. if (!vis[v]) 109. { 110. q.push (v); 111. vis[v] = 1; 112. } 113. } 114. } 115. } 116. return d[t]; 117. } 118.} d,g; 119. 120./****************************************以最短路建圖*********************************/ 121.void make_map (int n) 122.{ 123. for (int i = 1; i <= n; ++i) 124. { 125. for (int j = 0;j < d.u[i].size();++j) 126. { 127. int v = d.u[i][j].v, w = d.u[i][j].w; 128. if (d.d[v] - d.d[i] == w) 129. { 130. q.AddEdge(i,v,1);//最大流,流量是1 131. g.AddEdge(i,v,1);//最短路,邊權是1 132. } 133. } 134. } 135.} 136./************************************************************************************/ 137.int main() 138.{ 139. int n, m; 140. while (~scanf ("%d %d", &n, &m) ) 141. { 142. d.init (n); 143. g.init (n); 144. mem (q.v, 0); 145. mem (q.used,0); 146. for (int i = 0, u, v, w; i < m; ++i) 147. { 148. scanf ("%d %d %d", &u, &v, &w); 149. d.AddEdge (u, v, w); 150. } 151. int minway = d.spfa (1, n, n); 152. make_map (n); 153. int ans1 = q.MaxFlow (1, n); 154. int ans2 = m - g.spfa (1, n, n); 155. printf ("%d %d\n", ans1, ans2); 156. } 157. return 0; 158.} 159./* 160. 161.7 12 162.1 2 1 163.2 7 8 164.1 7 8 165.1 6 7 166.6 7 1 167.2 3 3 168.3 6 3 169.6 4 1 170.7 4 2 171.1 5 2 172.5 4 4 173.7 5 6 174. 175.*/ bfs版的增廣路: 1.#define mem(a,x) memset(a,x,sizeof(a)) 2.#include<iostream> 3.#include<cstdio> 4.#include<cstring> 5.#include<algorithm> 6.#include<queue> 7.#include<set> 8.#include<stack> 9.#include<cmath> 10.#include<map> 11.#include<stdlib.h> 12.#include<cctype> 13.#include<string> 14.using namespace std; 15.typedef long long ll; 16.const int N = 2000; 17.const int inf = 1<<27; 18.struct Node 19.{ 20. int v, w; 21. Node (int v = 0,int w = 0):v(v),w(w){} 22.}; 23.struct Spfa 24.{ 25. int d[N+5]; 26. bool vis[N+5]; 27. queue<int>q; 28. vector<Node>u[N+5]; 29. void init(int n) 30. { 31. while (!q.empty()) q.pop(); 32. for (int i = 0;i <= n;++i) u[i].clear(); 33. mem(vis,0); 34. } 35. void AddEdge(int uu,int vv,int ww) 36. { 37. u[uu].push_back(Node(vv,ww)); 38. u[vv].push_back(Node(uu,ww)); 39. } 40. int spfa(int s, int t,int n) 41. { 42. for (int i = 0; i <= n; ++i) d[i] = inf; 43. q.push (s); 44. vis[s] = 1; 45. d[s] = 0; 46. while (!q.empty() ) 47. { 48. int h = q.front(); 49. q.pop(); 50. vis[h] = 0;//spfa不一樣於bfs 51. for (int i = 0; i < u[h].size(); ++i) 52. { 53. int &v = u[h][i].v, &w = u[h][i].w; 54. if (d[v] > d[h] + w) 55. { 56. d[v] = d[h] + w; 57. if (!vis[v]) 58. { 59. q.push (v); 60. vis[v] = 1; 61. } 62. } 63. } 64. } 65. return d[t]; 66. } 67.} d,g; 68.struct Edge 69.{ 70. int from, to, cap, flow; 71. Edge (int u = 0, int v = 0, int c = 0, int f = 0) : from (u), to (v), cap (c), flow (f) {} 72.}; 73.struct EdmondsKarp 74.{ 75. int n, m; 76. vector<Edge>edges;//邊數的兩倍 77. vector<int>G[N+5];//鄰接表,G[i][j]表示節點i的第j條邊在e數組中的序號 78. int a[N+5];//當起點到i的可改進量 79. int p[N+5];//最短路樹上p的入弧編號 80. void init (int n) 81. { 82. for (int i = 0; i <= n; ++i) G[i].clear(); 83. edges.clear(); 84. } 85. void AddEdge (int from, int to, int cap) 86. { 87. edges.push_back (Edge (from, to, cap, 0) ); 88. edges.push_back (Edge (to, from, 0, 0) ); //反向弧 89. m = edges.size(); 90. G[from].push_back (m - 2); 91. G[to].push_back (m - 1); 92. } 93. int MaxFlow (int s, int t) 94. { 95. int flow = 0; 96. while (1) 97. { 98. mem (a, 0); 99. queue<int>Q; 100. Q.push (s); 101. a[s] = inf; 102. while (!Q.empty() ) 103. { 104. int x = Q.front(); 105. Q.pop(); 106. for (int i = 0; i < G[x].size(); ++i) 107. { 108. Edge & e = edges[G[x][i]]; 109. if (!a[e.to] && e.cap > e.flow) 110. { 111. p[e.to] = G[x][i]; 112. a[e.to] = min (a[x], e.cap - e.flow); 113. Q.push (e.to); 114. } 115. } 116. if (a[t]) break; 117. } 118. if (!a[t]) break; 119. for (int u = t; u != s; u = edges[p[u]].from) 120. { 121. edges[p[u]].flow += a[t]; 122. edges[p[u] ^ 1].flow -= a[t]; 123. } 124. flow += a[t]; 125. } 126. return flow; 127. } 128.}q; 129.void make_map (int n) 130.{ 131. for (int i = 1; i <= n; ++i) 132. { 133. for (int j = 0;j < d.u[i].size();++j) 134. { 135. int v = d.u[i][j].v, w = d.u[i][j].w; 136. if (d.d[v] - d.d[i] == w) 137. { 138. q.AddEdge(i,v,1);//最大流,流量是1 139. g.AddEdge(i,v,1);//最短路,邊權是1 140. } 141. } 142. } 143.} 144.int main() 145.{ 146. int n, m; 147. while (~scanf ("%d %d", &n, &m) ) 148. { 149. d.init (n); 150. g.init (n);q.init(n); 151. for (int i = 0, u, v, w; i < m; ++i) 152. { 153. scanf ("%d %d %d", &u, &v, &w); 154. d.AddEdge (u, v, w); 155. } 156. int minway = d.spfa (1, n, n); 157. make_map (n); 158. int ans1 = q.MaxFlow (1, n); 159. int ans2 = m - g.spfa (1, n, n); 160. printf ("%d %d\n", ans1, ans2); 161. } 162. return 0; 163.} 164./* 165. 166.7 12 167.1 2 1 168.2 7 8 169.1 7 8 170.1 6 7 171.6 7 1 172.2 3 3 173.3 6 3 174.6 4 1 175.7 4 2 176.1 5 2 177.5 4 4 178.7 5 6 179.*/
codeforces 14D
題意: 本題目就是要求出一棵樹上面兩條路徑長度的乘積,輸出最大的乘積便可。 思路: 枚舉每條邊,對於每一條邊,我均可以求出斷開這條邊之後獲得的兩條最長的路徑的長度。其中的最大值就是答案。 代碼 #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; vector<int >ve[300]; int n; int vis[300]; int dis[300]; int tar[300]; int zux,zuy; bool ok(int x,int y) { if(x==zux&&y==zuy) return 1; else if(x==zuy&&y==zux) return 1; return 0; } void dfs(int num) { tar[num]=1; vis[num]=1; for(int i=0;i<ve[num].size();i++) { int x=ve[num][i]; if(vis[x]||ok(x,num)) continue; dis[x]=dis[num]+1; dfs(x); } } int pac(int x) { memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); dfs(x); int ans=0; for(int i=1;i<=n;i++) { if(dis[ans]<dis[i]) ans=i; } memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); dfs(ans); ans=0; for(int i=1;i<=n;i++) { if(ans<dis[i]) ans=dis[i]; } return ans; } int solve() { memset(tar,0,sizeof tar); int t1=0,t2=0; if(ve[zux].size()==1) t1=0; else t1=pac(zux); if(ve[zuy].size()==1) t2=0; else t2=pac(zuy); return t1*t2; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) ve[i].clear(); int x,y; for(int i=0;i<n-1;i++) { scanf("%d%d",&x,&y); ve[x].push_back(y); ve[y].push_back(x); } int ans=0; for(int i=1;i<=n;i++) { for(int j=0;j<ve[i].size();j++) { zux=i; zuy=ve[i][j]; ans=max(ans,solve()); } // printf("ans:%d\n",ans); } printf("%d\n",ans); } return 0; }
數論
hdu2161
題意:求某個數的最大質因子是第幾個質數 思路:用篩法打個帶標記的質數表便可 代碼: #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<iomanip> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<vector> #include<set> #include<map> using namespace std; const int MAXN = 1000000; int str[MAXN + 10], prime[MAXN]; int Max(int a, int b) { return a>b ? a : b; } void init() { memset(str, 0, sizeof str); int cnt = 1; str[0] = 0; str[1] = 0; for (int i = 2; i <= MAXN; i++) { if (str[i] == 0) { str[i] = cnt; prime[cnt++] = i; } for (int j = 1; j < cnt && i * prime[j] <= MAXN; j++) { str[i * prime[j]] = Max(j, str[i]); if (i % prime[j] == 0) { break; } } } } int main() { init(); int n; while (~scanf("%d", &n)) { printf("%d\n", str[n]); } return 0; }
hdu3519(矩陣快速冪)
硬幣全部可能排列狀況爲2^n,朝向相同硬幣連續三個如下 的排列狀況爲a(1)=2,a(2)=4,a(3)=6,a(4)=10,觀察或者推算得a(n)=a(n-1)+a(n-2); 那麼連續三個或三個以上硬幣的全部組合狀況 爲b(n)=2^n-a(n); 化爲只含b(n)的遞推式即 b(n)=b(n-1)+b(n-2)+2^(n-2);b(1)=0;b(2)=0;b(3)=2;b(4)=6;用矩陣快速冪可快速算得b(n); 代碼 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; const int mod=10007; struct node { int a[4][4]; node() { a[1][1]=a[1][2]=a[1][3]=a[2][1]=1; a[2][2]=a[2][3]=a[3][1]=a[3][2]=0; a[3][3]=2; } void init() //將其初始化爲單位矩陣 { memset(a,0,sizeof(a)); for(int i=1; i<4; i++) a[i][i]=1; } }; node mul(node a,node b) //(a*b)%mod 矩陣乘法 { node ans; for(int i=1; i<4; i++) for(int j=1; j<4; j++) { ans.a[i][j]=0; for(int k=1; k<4; k++) ans.a[i][j]+=a.a[i][k]*b.a[k][j]; ans.a[i][j]%=mod; } return ans; } node power(node a,int n) //(a^n)%mod //矩陣快速冪 { node ans; ans.init(); while(n) { if(n%2)//n&1 ans=mul(ans,a); n/=2; a=mul(a,a); } return ans; } int main() { int i,j,n; while(~scanf("%d",&n)) { if(n<3){printf("0\n");continue;} node ans; ans=power(ans,n-2); printf("%d\n",(ans.a[1][3]*2)%mod); } return 0; }
SGU 106(擴展歐幾里得)
這題交的時候要注意是%I64d 因此我直接用了cin cout /* 方程兩邊同時除以gcd(a,b).咱們假設aa=a/gcd(a,b),bb=b/gcd(a,b),nn=n/gcd(a,b) 因此方程兩邊同時除以gcd(a,b)後, 能夠獲得一個方程aa*x+bb*y=nn. 而且該方程aa*x+bb*y=nn的解x,y就是a*x+b*y=n的解 咱們只要求解出aa*x+bb*y=1的其中一個解,設這兩個解爲x0,y0. 那麼aa*x+bb*y=nn的其中一個解解就是x0*nn,y0*nn. 接着,a*x+b*y=n的其中一個解解也就是x0*nn,y0*nn. a*(x0*nn)+b*(y0*nn)=n. a*(x0*nn+1*b)+b*(y0*nn-1*a)=n a*(x0*nn-1*b)+b*(y0*nn+1*a)=n. 繼續擴展 a*(x0*nn+k*b)+b*(y0*nn-k*a)=n (k屬於整數) nn=n/gcd(a,b). x=x0*nn+k*b y=y0*nn-k*a */ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> #include<vector> using namespace std; long long gcd(long long a,long long b) { return b==0?a:gcd(b,a%b); } long long exgcd(long long a,long long b,long long &x,long long &y) { if (!b) { x=1; y=0; return a; } long long gcd=exgcd(b,a%b,x,y); long long temp=x; x=y; y=temp-a/b*y; return gcd; } long long upper(long long a,long long b)//往上取整 { if (a<=0) return a/b; return (a-1)/b+1; } long long lower(long long a,long long b)//往下取整 { if (a>=0) return a/b; return (a+1)/b-1; } long long get(long long l,long long r,long long d,long long &k1,long long &k2) { if (d<0) { l=-l; r=-r; d=-d; swap(l,r); } k1=upper(l,d); k2=lower(r,d); } int main() { long long a,b,c,x1,y1,x2,y2; while (cin>>a>>b>>c)//ax+by+c=0 >>ax+by=-c { bool flag=0; c=-c; cin>>x1>>x2>>y1>>y2; long long ans,x,y; if (!a && !b && !c) { cout<<(x2-x1+1)*(y2-y1+1)<<endl; continue; } else if (!a && !b) { cout<<0<<endl; continue; } else if (!a) { if (c%b || c/b<y1 || c/b>y2) ans=0; else ans=x2-x1+1; cout<<ans<<endl; continue; } else if (!b) { if (c%a || c/a<x1 || c/a>x2) ans=0; else ans=y2-y1+1; cout<<ans<<endl; continue; } /* 先處理了a,b,c爲0的狀況 */ long long g=gcd(a,b); if (c%g)//若是 c不是gcd(a,b)的倍數 無解 { cout<<0<<endl; continue; } a/=g; b/=g; c/=g;// c=c/g 上面已經將c變爲-c exgcd(a,b,x,y); long long k1,k2,k3,k4; x*=c; y*=c; get(x1-x,x2-x,b,k1,k2); get(y1-y,y2-y,-a,k3,k4); // cout<<x<<" "<<y<<endl; ans=min(k2,k4)-max(k1,k3)+1; cout<<ans<<endl; } return 0; }
codeforces 300E
題意: 給定n個數(a1,a2……an),求p,其中p=n!,且p能夠被(a1!*a2!*…….*an!)整除。 分析: 將a1!*a2!*……*an!分解成質因子相乘的形式,可是確定不可能一個數一個數分解,巧妙的運用從大數轉換成小數的思想,求出各個質因子及其個數。再運用二分求解對於每一個質因子而言最小的nn,全部的nn取最大即爲結果 源代碼: #include<cstdio> #include<cstring> #include<string> #include<queue> #include<map> #include<cmath> #include<stack> #include<vector> #include<set> #include<iostream> #include<algorithm> using namespace std; const int N=1000005; typedef __int64 LL; LL num[N*10]; int p[N*10]; bool vis[N*10]; int prime[N]; int tot=0; LL sum; int ma; void init() { memset(vis,false,sizeof vis); for(int i=2; i<N*10; i++) { if(!vis[i]) { prime[tot++]=i; p[i]=i; } for(int j=0; j<tot&&i<=N*10/prime[j]; j++) { vis[i*prime[j]]=true; p[i*prime[j]]=prime[j]; if(i%prime[j]==0) break; } } } void cal() { for(int i=ma;i>=2;i--) { if(p[i]!=i) { num[p[i]]+=num[i]; num[i/p[i]]+=num[i]; } } } bool ok(LL m,int a,LL n) { LL res=0; while(m) { res+=m/a; m/=a; if(res>=n) return true; } return false; } LL BST(int a,LL n) { LL l=1,r=sum; LL ans=-1; while(l<=r) { LL mid=(l+r)/2; if(ok(mid,a,n)) { ans=mid; r=mid-1; } else l=mid+1; } return ans; } LL solve() { LL ans=1; for(int i=0;i<tot;i++) { if(num[prime[i]]) { ans=max(ans,BST(prime[i],num[prime[i]])); } else break; } return ans; } int main() { int n; init(); while(~scanf("%d",&n)) { memset(num,0,sizeof num); ma=0; sum=0; for(int i=0; i<n; i++) { int x; scanf("%d",&x); ma=max(ma,x); num[x]++; sum+=x; } for(int i=ma-1;i>=2;i--) { num[i]+=num[i+1]; } cal(); printf("%I64d\n",solve()); } return 0; }
Codeforces 696C(機率+組合數學+指數循環節)
題意:有三個杯子倒扣於桌面,放的位置編號1,2,3,剛開始2號地方的杯子裏有一個key,如今每次操做爲從一、3號位置上等機率選擇一個與2號位置的杯子交換(換杯子,位置編號仍是不變,可認爲左、中、右三個,好比3換到1就是右邊的杯子換到中間),求n次操做後key在2號位子的杯子裏的機率,結果用分數。 題解:每次都是從一、3中選一個,顯然機率就是1/2,並且每次2號位置的杯子都會變化。那麼dp[i][n]表示第n次操做後,key在i號位置的杯子中的機率。 顯然三個杯子,i=1,2,3;把換成一維的:f1(n),f2(n),f3(n).而後遞推: 對於f1(n):可能第n-1次後key就在1位置,那麼第n次只能選3號,即1/2*f1(n-1);可能第n-1次後key在2號中,那麼只能選1位置,即1/2*f2(n-1);只有這兩個可能到達,因此f1(n)=1/2*(f1(n-1)+f2(n-1)); 對於f3(n),位置是對稱的:f3(n)=f1(n); 相似1,f2(n)=1/2*(f1(n-1)+f3(n-1)); 咱們要求的是f2(n) 上面三個等式:f2(n)=1/2*(f2(n-1)+f2(n-2)); 這個式子用組合數學求解線性常係數遞推式(母函數內容) 最後f2(n)= . 題目對取模要求比較變態,必須是分子分母分別取模前要求化爲最簡分式,最簡後再分別取模。上面的式子問題在於分母的3,由於2^(n-1)與分子已經沒有公因子能夠消了。那麼看分子裏面有沒有3這個因子能夠消: 很巧的是:當n-1爲奇數時,2^(n-1)%3=2,此時n爲偶數,(-1)^n=1;因此(2^(n-1)+(-1)^n)%3=0,同理n-1位偶數時有一樣的結論 也就是說分子必然能消掉3;既然能整除,那麼就把(2^(n-1)+(-1)^n)/3總體當作分子,並且是能夠用逆元去處理3的,這樣處理後就是最簡分式了 即 ,inv是逆元,分子分母都是對1e9+7取模 如今問題就是n很大(不知道這裏能用java搞),冪很大就是用指數循環節: 要求各歐拉函數 注意定理使用的條件,n較小時直接快速冪; #pragma comment(linker, "/STACK:10240000,10240000") #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<set> #include<vector> #include<map> #include<stack> #include<cmath> #include<algorithm> using namespace std; const double R=0.5772156649015328606065120900; const int N=1e5+5; const int mod=1e9+7; const int INF=0x3f3f3f3f; const double eps=1e-8; const double pi=acos(-1.0); typedef long long ll; int n; ll a[N],ini,M; ll judge() { ll tm=mod+10; ll ans=1; for(int i=0;i<n;i++) { if(ans>tm/a[i]) return 0;//很大 ans*=a[i]; } return ans; } ll Pow(ll a,ll n) { ll ans=1; while(n) { if(n&1) ans=ans*a%mod; a=a*a%mod; n>>=1; } return ans; } void solve(ll mo,ll tag) { ll ans=1; for(int i=0;i<n;i++) { a[i]%=mo;//注意爆數據 ans=ans*a[i]%mo; } ans--; ans=(ans%mo+mo)%mo; if(ans==0) ans+=mo; ll down=Pow(2,ans); ll up=down+tag; up=up*ini%mod; cout<<up<<'/'<<down<<endl; } int main() { ini=333333336,M=1000000006;//事先求好逆元和歐拉值 while(cin>>n) { int tag=-1; for(int i=0;i<n;i++) { scanf("%I64d",&a[i]); if(a[i]%2==0) tag=1;// } ll all=judge(); if(all==0) {solve(M,tag);continue;}//冪很大 if(all==1) { cout<<"0/1"<<endl; continue; } ll down=Pow(2,all-1); ll up=(down+tag)*ini%mod; cout<<up<<'/'<<down<<endl; } return 0; }
Hdu 5728 PowMod(歐拉函數+指數循環節)
題意:只是那個k是無限個 題解:首先是算k:先了解兩條性質,歐拉函數是積性函數; (1)積性函數性質:F(m1*m2)=F(m1)*F(m2),當且近當gcd(m1,m2)=1時成立; (2) ,其中p是n的素因子,且gcd(p,n/p)=1。這個用歐拉函數的素因子表達式很好證實。 有了這個再來算k,題目的n是很特殊的,它的每一個素因子的冪次都是1: 那麼假設素數p是n的一個素因子,顯然gcd(p,n/p)=1;關鍵是i,若是i中沒有素因子p,那麼就直接用積性性質。若是i%p==0,必然能夠寫成i=k*p;即倍數關係,不然i%p!=0; 因此分紅兩部分求: .....p是素數,素數的歐拉值就是p-1; 到這裏前兩和式是能夠合併的,考慮和式的上下限,含義不一樣,第二項的i表示的p的倍數i*p纔是第一項i的含義,至關於第二項恰好把第一項補齊了,那麼從1到m沒有遺漏,並且第二項的i用第一項替換后里面也是n/p;最終 這是個e二元遞歸式:n/p和m/p當作總體,那麼設原先求的爲f(n,m),因此f(n,m)=(p的歐拉值)*f(n/p,m)+f(n,m/p); 每次枚舉一個就夠了,n每次要除以p,最多就是把它的每一個素因子除完。 第二部分k的超級冪:用歐拉的定理:指數循環節 每次往冪次上一層模就取一次歐拉值,只有1的歐拉值等一本身,其餘數的歐拉值都是小於本身的,因此模會不斷變小至1,顯然對1取模結果就是0,因此無限就變得有限了 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int INF=1e9+7; const double eps=1e-7; const int N=1e7+5; const int M=1000000007; typedef long long ll; int pri[N],phi[N]; bool vis[N]; int tot; ll sum[N]; void init() { int n=N; tot=0; memset(vis,false,sizeof vis); phi[1]=1; for(int i=2;i<n;i++) { if(!vis[i]) { pri[tot++]=i; phi[i]=i-1; } for(int j=0;j<tot && i*pri[j]<n;j++) { vis[i*pri[j]]=true; if(i%pri[j]==0) { phi[i*pri[j]]=phi[i]*pri[j]; break; } else phi[i*pri[j]]=phi[i]*(pri[j]-1); } } sum[0]=0; for(int i=1;i<N;i++) sum[i]=(sum[i-1]+phi[i])%M; } ll Pow(ll a,ll n,ll mod) { ll ans=1; while(n) { if(n&1) { ans=ans*a%mod; // if(ans>=mod)ans=ans%mod; } a=a*a%mod; // if(a>=mod) a=a%mod; n>>=1; } if(ans==0) ans+=mod; return ans; } ll solve(ll k,ll mod) { if(mod==1) return mod; ll tmp=phi[mod]; ll up=solve(k,tmp); ll ans=Pow(k,up,mod); return ans; } int rear; int a[15]; void resolve(ll n) { for(int i=0;i<tot;i++) { if(!vis[n]) {a[rear++]=n;break;} if(n%pri[i]==0) { a[rear++]=pri[i]; n/=pri[i]; } } } ll f(int pos,ll n,ll m) { //pos即每一個素數,一次一個就好了 if(n==1) return sum[m];//n爲1結果就是歐拉值的前綴和 if(m==0)return 0; return ((a[pos]-1)*f(pos-1,n/a[pos],m)%M+f(pos,n,m/a[pos]))%M; } int main() { init();//打表 ll n,m,p; while(~scanf("%I64d%I64d%I64d",&n,&m,&p)) { rear=0; resolve(n);//素因子分解 ll k=f(rear-1,n,m);//算k ll ans=solve(k,p); printf("%I64d\n",ans%p); } return 0; }
dp
HDU2845
題意:求在一個矩陣中能吃掉數字的最大和,當吃掉某個數字的時候,該數字的上一行(如果第0行則不處理)和下一行(如果最後一行不處理)和左右兩個數字(同理)會消失 思路:先按吃一個則左右消失的規則處理出每一行能吃到的最大數字和(能夠用DP),而後再按照吃一行上下兩行消失的規則處理行的和 代碼: #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<iomanip> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<vector> #include<set> #include<map> using namespace std; const int MAXN = 200010; int n, m,str[MAXN]; int Max(int a,int b) { return a>b?a:b; } int main() { while (~scanf("%d%d", &n, &m)) { int a[MAXN], b[MAXN]; a[0] = b[0] = 0; for (int i = 1; i <= n; i++) { int temp; for (int j = 1; j <= m; j++) { scanf("%d", &temp); a[j]=Max(a[j-1],b[j-1]); b[j]=a[j-1]+temp; } str[i]=Max(a[m],b[m]); } a[0]=b[0]; for(int i=1;i<=n;i++) { a[i]=Max(a[i-1],b[i-1]); b[i]=a[i-1]+str[i]; } printf("%d\n",Max(a[n],b[n])); } return 0; }
po2677(雙調歐幾里得旅行商問題)
題意: 給出n個點,要求從最左端點嚴格向右走到最右端點,再從最右端點走回來 途中必須通過全部點(保證每一個點橫座標不一樣),求最短路。 解題思路: 雙調歐幾里得旅行商問題,dp。將點按橫座標從小到大排序,dp[i][j]表示從j向左走到1,再從1走到i的最短路(i<j)(此過程當中1-j全部的點都被經歷過)。 給出轉移方程: dp[i][j]=dp[i][j-1]+dist(j-1,j) (i<=j-2); dp[j-1][j]=min(dp[j-1][j],dp[i][j-1]+dist(i,j)) (i<=j-2); 按此方式更新不會丟失最優解(每次只把j歸入集合而不考慮j+1,j+2...) 最終獲得dp[n][n]=dp[n-1][n]+dist(n-1,n); 代碼: 1. #include<iostream> 2. #include<cstring> 3. #include<cmath> 4. #include<iomanip> 5. using namespace std; 6. const double INF=1.0*9999999; 7. struct Node 8. { 9. int x,y; 10. }; 11. Node a[1005]; 12. double dp[1005][1005]; 13. int n; 14. double dist(int i,int j) 15. { 16. return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)); 17. } 18. int main() 19. { 20. while(~scanf("%d",&n)) 21. { 22. for(int i=1;i<=n;i++) 23. scanf("%d%d",&a[i].x,&a[i].y); 24. memset(dp,INF,sizeof dp); 25. dp[1][2]=dist(1,2); 26. for(int j=3;j<=n;j++) 27. { 28. for(int i=1;i<=j-2;i++) 29. dp[i][j]=dp[i][j-1]+dist(j-1,j); 30. dp[j-1][j]=INF; 31. for(int i=1;i<=j-2;i++) 32. dp[j-1][j]=min(dp[i][j-1]+dist(i,j),dp[j-1][j]); 33. } 34. dp[n][n]=dp[n-1][n]+dist(n-1,n); 35. cout<<setprecision(2)<<fixed<<dp[n][n]<<endl; 36. } 37. }
hdu2059
題意: 額,中文題不翻譯。 解題思路: 看別人的博客才理解的,因爲每一個加油站均可以選擇加油和不加油,爲了把全部狀況考慮進去,不妨令dp[i]爲從起點到第i個加油站所須要的最短期,那麼dp[i]就應該是從0到i-1這些節點充滿電而後直接跑到i的最短期。爲何這樣作不會丟失最優解?不妨考慮第4個節點,計算過程當中你求出了dp[2]+t2和dp[3]+t3,這就等於說你已經考慮了第3個節點充電或者不充電的狀況, 並且此時的dp[2]是它所能取得最小值,同理,dp[1]+t1和dp[2]+t2就表示考慮了第2個節點充電或者不充電的狀況,那麼有沒有考慮第2個節點不充電可是第三個節點充電的狀況呢?這裏看上去是沒有,可是你在計算dp[3]的時候,就已經考慮好了第二個點究竟該不應充電,因此第2個節點不充電可是第三個節點充電的狀況可能會包含於dp[3]+t3中,好繞口啊。。 1. #include<cstdio> 2. #include<cstring> 3. #include<string> 4. #include<iostream> 5. #include<sstream> 6. #include<algorithm> 7. #include<utility> 8. #include<vector> 9. #include<set> 10. #include<map> 11. #include<queue> 12. #include<cmath> 13. #include<iterator> 14. #include<stack> 15. using namespace std; 16. const double INF=9999999; 17. const double eps=1e-7; 18. const int mod=1000007; 19. const int maxn=50000; 20. double dp[maxn]; 21. int a[maxn]; 22. int main() 23. { 24. int l,n,c,t,vr,v1,v2; 25. while(cin>>l) 26. { 27. cin>>n>>c>>t; 28. cin>>vr>>v1>>v2; 29. for(int i=1;i<=n;i++) 30. cin>>a[i]; 31. memset(dp,INF,sizeof dp); 32. sort(a+1,a+1+n); 33. a[0]=0; 34. a[n+1]=l; 35. dp[0]=0; 36. for(int i=1;i<=n+1;i++) 37. { 38. for(int j=0;j<i;j++) 39. { 40. double tt; 41. if(c>a[i]-a[j]) 42. tt=1.0*(a[i]-a[j])/v1; 43. else 44. tt=1.0*c/v1+1.0*(a[i]-a[j]-c)/v2; 45. dp[i]=min(dp[i],dp[j]+tt); 46. } 47. dp[i]+=t;//默認充滿電 48. } 49. 50. if(dp[n+1]-t>1.0*l/vr) 51. cout<<"Good job,rabbit!"<<endl; 52. if(dp[n+1]-t<1.0*l/vr) 53. cout<<"What a pity rabbit!"<<endl; 54. } 55. }
hdu5076(dp+狀態壓縮)
題意: 旅行商問題,給出n個位置,求從起點(0,0)出發遍歷全部位置再回到原點得最短路。 解題思路: 1.DP+狀態壓縮,以二進制保存當前遍歷狀態,若是此狀態下某個點未被通過,則用此狀態轉移到通過該店的狀態,更新其值。 2.注意到n很小,DFS搜一遍~。 代碼1: 1. #include <iostream> 2. #include <cstdio> 3. #include <cstring> 4. #include <algorithm> 5. #include <cmath> 6. using namespace std; 7. const int N=55; 8. const int INF=0x3f3f3f3f; 9. int dp[(1<<11)+15][N]; 10. int dist[N][N]; 11. struct Node 12. { 13. int x,y; 14. }p[N]; 15. int main() 16. { 17. int n,m,t; 18. int cnt; 19. while(cin>>n>>m) 20. { 21. 22. cnt=0; 23. for(int i=0; i<n; i++) 24. for(int j=0; j<n; j++) 25. { 26. cin>>t; 27. if(t||(i==0&&j==0)) 28. { 29. p[cnt].x=i; 30. p[cnt++].y=j; 31. } 32. } 33. for(int i=0; i<cnt; i++) 34. { 35. for(int j=i+1; j<cnt; j++) 36. dist[i][j]=dist[j][i]=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y); 37. dist[i][i]=0; 38. } 39. memset(dp,INF,sizeof(dp)); 40. dp[0][0]=0; 41. for(int i=0; i<(1<<cnt); i++) 42. { 43. for(int j=0; j<cnt; j++) 44. { 45. if(dp[i][j]==INF) 46. continue; 47. for(int k=0; k<cnt; k++) 48. { 49. if(i&(1<<k)) 50. continue; 51. dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dist[j][k]); 52. } 53. } 54. } 55. cout<<dp[(1<<cnt)-1][0]<<endl; 56. } 57. }
uva10118(分析+狀壓)
題意:桌上有4堆糖果,每堆糖果高度不超過40,每顆糖果有一種顏色(一共20種,1,2,3...,20), 有一個籃子,一開始是空的,每當裏面裝有兩顆顏色相同的糖果時,就能夠從籃子裏拿出這一對糖果。 若是籃子裏的糖果數量爲5個而且不能再拿時,籃子充滿,遊戲結束。問最多能拿走多少對糖果。 糖果堆上的全部糖果拿光了也算結束 樣例: 5 1 2 3 4 1 5 6 7 2 3 3 3 4 9 8 6 8 7 2 1 解釋: 每堆糖果高度爲5, 從左往右爲4堆糖果,數字表明顏色。堆頂在上。 結果: 8 一開始看到這個題會以爲狀態根本就沒法表示,由於你須要保存每一堆的剩餘層數,還須要保存籃子裏的糖果數量和顏色,即便狀態存下了,轉移起來時間複雜度也過高了。 41^4* 21^5實在太大。 後來想一想根本不用保存這麼多狀態。由於保存了每堆剩餘的層數就知道了拿掉的糖果,又由於籃子裏同種顏色的糖果只能是0或者1(同色糖會被拿掉),因此能夠推出籃子裏糖果的狀態。 將糖果編號改成0到19,而後用狀壓表示每一個狀態(每堆剩餘層數)對應籃子裏的糖果。 #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define all(x) (x).begin(), (x).end() #define for0(a, n) for (int (a) = 0; (a) < (n); (a)++) #define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++) #define ysk(x) (1<<(x)) typedef long long ll; typedef pair<int, int> pii; const int INF =0x3f3f3f3f; const int maxn=40 ; int a[5][maxn+3]; int state[maxn+10][maxn+10][maxn+10][maxn+10]; int dp[maxn+3][maxn+3][maxn+3][maxn+3]; int n; int getNum(int &s)//計算籃子s裏的糖果數量 { int cnt=0; for(int i=0;i<20;i++) if(s&ysk(i)) { cnt++; } return cnt; } void fix(int &x,int y)//更新dp值 { if(y>x) x=y; } int update(const int* hp,const int* h,int p )//從狀態hp轉移到狀態h,在第p堆轉移。 { int s,sp=state[hp[1] ][hp[2] ][hp[3] ][hp[4] ]; int k= a[p][hp[p]];//k表示拿掉糖果的顏色。 if(sp&ysk(k) ) { s=sp^ysk(k); fix( dp[h[1]][h[2] ][h[3] ][h[4] ], dp[hp[1] ][hp[2] ][hp[3]][hp[4] ]+1 ); } else{ int num=getNum(sp); if(num==4) return 0; s=sp^ysk(k); fix( dp[h[1]][h[2] ][h[3] ][h[4] ], dp[hp[1] ][hp[2] ][hp[3]][hp[4] ] ); } state[h[1]][h[2]] [h[3] ][h[4] ]=s; return 1; } int work() { int ans=0; state[n][n][n][n]=0; memset(dp,-1,sizeof dp); dp[n][n][n][n]=0; int h[5]; for(h[1]=n;h[1]>=0;h[1]-- ){ for(h[2]=n;h[2]>=0;h[2]--){ for(h[3]=n;h[3]>=0;h[3]--){ for(h[4]=n;h[4]>=0;h[4]--){ if(h[1]+h[2]+h[3]+h[4]==4*n) continue; // dp[h[1] ][h[2]][h[3]][h[4] ]=-1; for(int i=1;i<=4;i++) if( h[i]+1<=n ) { int hp[5]; memcpy(hp,h,sizeof hp); hp[i]++; if(dp[hp[1] ][hp[2] ][hp[3] ][hp[4] ]==-1) continue; update(hp,h,i); } ans=max(ans,dp[h[1] ][h[2]][h[3]][h[4] ]); } } } } return ans; } int main() { while(~scanf("%d",&n)&&n) { for(int h=n;h>=1;h-- ) { for(int i=1;i<=4;i++) { scanf("%d",&a[i][h]);//第i堆,第h層糖果顏色 a[i][h]--; } } printf("%d\n",work()); } return 0; } /* 5 1 2 3 4 1 5 6 7 2 3 3 3 4 9 8 6 8 7 2 1 對於這組樣例的分析: 若是從左往右,高度依次爲 0 0 4 5 能夠發現這時是個死局 由於籃子裏有5顆顏色各不相同的糖果。 能夠認爲該狀態沒法達到,由於即便達到了也不能進行後續轉移,而且這個狀態的最後一步是沒有消除糖果的,因此這個狀態 相比於前面轉移到它的狀態並不更優, 因此說能夠認爲該狀態達不到,這對於結果毫無影響。 假如狀態(0 ,0 ,4, 5)沒法達到,那麼狀態(0,0,4,4)還須要繼續計算 ,(0,0,4,4)是能夠到達的。 從結果上看,(0,0,4,4)籃子裏只有4顆糖 可是(0,0,4,4)不可能從(0,0,4,5)轉移過來的,徹底能夠從(1,0,4,4)轉移過來。最後一步不一樣,表明着拿取糖果的順序不一樣。 還有一點須要注意的是,初始化是,應該所有memset(dp,-1,sizeof dp) 不然可能會受到上一組樣例的影響 好比說考慮結果(0,0,3,3),枚舉這個狀態的前驅,若是狀態(0,0,3,4)在這個樣例裏是根本不可達的,甚至能達到的狀態距離它 還相距甚遠,可是在上一組樣例裏這個狀態能夠達到的,那麼它的dp值就不爲-1,會從上一個樣例的狀態(0,0,3,4)轉移到這個樣例的 (0,0,3,3),從而形成錯誤。 */