图论2 1010
最小生成树
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵恰好有need条白色边的权值和最小的生成树。题目保证有解。
对于所有数据,V,E<=100000,c为[1,1000]中的正整数。
题解
可以知道恰好选到need条白边就是最优的,考虑给所有白边加上一个值,随着值的增大,在生成树中的白边越少,所以可以二分。
取最后一次满足条件的值。
排序的时候,对于相同值的边白边优先。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn=100005; int n,m,need; int fa[maxn],sum,cnt; struct edge{ int x,y,c,val; bool operator < (const edge a) const { if(val==a.val) return c<a.c; return val<a.val; } }e[maxn],cy[maxn]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);} bool check(int x){ sum=0,cnt=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ cy[i]=e[i]; if(!e[i].c) cy[i].val+=x; } sort(cy+1,cy+m+1); for(int i=1;i<=m;i++){ int dx=find(cy[i].x),dy=find(cy[i].y); if(dx!=dy){ fa[dx]=dy; sum+=cy[i].val; cnt+=!cy[i].c; } } return cnt>=need; } int main(){ freopen("e.in","r",stdin); freopen("e.out","w",stdout); read(n);read(m);read(need); for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].val),read(e[i].c); int l=-1005,r=1005,ret; while(l<=r){ int mid=(l+r)/2; if(check(mid)) ret=sum-need*mid,l=mid+1;//写need else r=mid-1; } printf("%d",ret); }
很玄的一道题
贪吃蛇
有两条蛇(1 号蛇和 2 号蛇)在 n 行 m 列的地图上,地图上有障碍物。一条蛇碰到蛇身/障碍物/边界就会死。蛇身会不断长长——可以理解为蛇尾位置不会变,蛇只会向前伸展不会缩尾巴。两条蛇都绝顶聪明,如果自己能赢,一定会尽量快地赢;如果自己会输,一
定会死得尽量晚。给出初始局面,两蛇轮流走,每次可以且必须向上下左右移动一格。1 号蛇先走,请告诉我谁会在多少回合时赢。
对于100%的数据,1<=n<=20,1<=m<=20,0的个数不超过50个。
题解
用的是alpha-beta算法,还没怎么搞懂,就给个博客
代码T了两个点
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int inf=1000000; const int maxn=25; const int oo=400; int n,m; int mp[maxn][maxn]; int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; bool vis[maxn][maxn]; int maxmin(int step,int x1,int y1,int x2,int y2,int alpha,int beta){ bool opt=false; if(step&1){//提高下界 for(int i=0;i<4;i++){ int xx=x1+dx[i],yy=y1+dy[i],val=alpha; if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){ opt=true; vis[xx][yy]=true; val=maxmin(step+1,xx,yy,x2,y2,alpha,beta); vis[xx][yy]=false; } if(val>alpha) alpha=val; if(alpha>beta) return alpha; } if(opt) return alpha; } else {//缩小上界 for(int i=0;i<4;i++){ int xx=x2+dx[i],yy=y2+dy[i],val=beta; if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){ opt=true; vis[xx][yy]=true; val=maxmin(step+1,x1,y1,xx,yy,alpha,beta); vis[xx][yy]=false; } if(val<beta) beta=val; if(alpha>beta) return beta; } if(opt) return beta; } if(step&1) return step-oo; return oo-step; } int main(){ freopen("h.in","r",stdin); freopen("h.out","w",stdout); int x1,y1,x2,y2; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ scanf("%d",&mp[i][j]); if(mp[i][j]==1) {x1=i,y1=j;} else if(mp[i][j]==2) {x2=i,y2=j;} vis[i][j]=mp[i][j]; } int cx=maxmin(1,x1,y1,x2,y2,-inf,inf); if(cx>0) printf("1 %d",oo-cx); else printf("2 %d",cx+oo); }
信息拦截
Alice和Bob经常利用网络传播一些不法信息。网络由n台电脑组成,Alice拥有1号电脑,Bob拥有n号电脑。有m对电脑间可以通过电缆单向传输信息。为了不被拦截,Alice每次传送信息都可以选取任意一条路径(经过的电脑可以重复),把信息沿着电缆传送给Bob。现在政府获知了这一情报,他们可以侵入恰好1台电脑,并获取经过这条电脑的所有信息。政府希望(无论Alice选择的路线如何)他们对Alice发送的每条信息都恰好获取1次(如果太少,会漏掉重要情报;如果太多,他们的电脑会被塞满的)。现在你需要求出政府可以选择入侵哪些电脑以达到要求。
对于100%的数据,2<=n<=500000,0<=m<=1000000,t<=10。
可能有自环
题解
分析一下题目要求:选取一个点满足 不属于一个点数>1的强联通分量切没有自环(使得信息不多),是1到n的路径的必经点(使得信息不漏)
那么就可以理出做题步骤:1.tarjan缩点 2.判必经点。
因为不知道有向图的割点能不能用tarjan求,所以就换了《算法竞赛》上的方法:对于有向无环图,求出fs[x]起点到x的路径条数,ft[x]终点到x的路径条数,根据乘法原理:如果fs[x]*ft[x]=fs[t]那么x就是s到t的必经点。
但是路径条数增长很快,所以考虑使用hash的思想,对路径条数取模,可能会误判。
求fs和ft可以用拓扑排序来做,但是考虑不能到达n的点对整个图是有影响的,所以建新图的时候只能把能到达n的点放进去。
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=500005; const int maxm=1000005; const int mod=1000000007; int t,n,m; int cnt,head[maxn]; int cur,low[maxn],dfn[maxn]; int top,num,s[maxn],res[maxn],mp[maxn]; bool in[maxn],flag[maxn],circle[maxn]; ll fs[maxn],ft[maxn]; struct edge{ int x,y,next; }e[maxm],go[maxm],back[maxm]; int cntg,cntb,headg[maxn],headb[maxn]; int g[maxn],b[maxn]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } void add(int x,int y){ e[++cnt]=(edge){x,y,head[x]}; head[x]=cnt; } void addg(int x,int y){ go[++cntg]=(edge){x,y,headg[x]}; headg[x]=cntg; } void addb(int x,int y){ back[++cntb]=(edge){x,y,headb[x]}; headb[x]=cntb; } void init(){ cnt=num=cntg=cntb=cur=top=0; memset(g,0,sizeof(g)); memset(b,0,sizeof(b)); memset(fs,0,sizeof(fs)); memset(ft,0,sizeof(ft)); memset(mp,0,sizeof(mp)); memset(res,0,sizeof(res)); memset(dfn,0,sizeof(dfn)); memset(head,0,sizeof(head)); memset(in,false,sizeof(in)); memset(headg,0,sizeof(headg)); memset(headb,0,sizeof(headb)); memset(circle,false,sizeof(circle)); } void tarjan(int x){ if(x==n) in[x]=true; dfn[x]=low[x]=++cur; s[++top]=x;flag[x]=true; for(int i=head[x];i;i=e[i].next){ int y=e[i].y; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(flag[y]) low[x]=min(low[x],dfn[y]); in[x]|=in[y]; } if(dfn[x]==low[x]){ num++; int kk; if(s[top]==x) mp[num]=x; do{ kk=s[top--]; res[kk]=num; flag[kk]=false; }while(kk!=x); } } void back_topsort(){ queue<int> q; ft[res[n]]=1; q.push(res[n]); while(!q.empty()){ int x=q.front(); q.pop(); for(int i=headb[x];i;i=back[i].next){ int y=back[i].y; b[y]--; ft[y]+=ft[x]; if(ft[y]>=mod) ft[y]-=mod; if(!b[y]) q.push(y); } } } void go_topsort(){ queue<int> q; fs[res[1]]=1; q.push(res[1]); while(!q.empty()){ int x=q.front(); q.pop(); if(mp[x]&&!circle[mp[x]]&&fs[x]*ft[x]%mod==ft[res[1]]) s[++top]=mp[x]; for(int i=headg[x];i;i=go[i].next){ int y=go[i].y; g[y]--; fs[y]+=fs[x]; if(fs[y]>=mod) fs[y]-=mod; if(!g[y]) q.push(y); } } } void solve(){ read(n);read(m); init(); for(int i=1;i<=m;i++){ int x,y; read(x);read(y); if(x==y) circle[x]=true; else add(x,y); } tarjan(1); // for(int i=1;i<=n;i++) printf("%d ",res[i]); // putchar(10); if(!dfn[n]||res[1]==res[n]) {printf("0\n\n");return ;}//没联通 for(int x=1;x<=n;x++) if(dfn[x]&&in[x]){ for(int i=head[x];i;i=e[i].next){ int y=e[i].y; if(in[y]&&res[x]!=res[y]){ addg(res[x],res[y]);g[res[y]]++; addb(res[y],res[x]);b[res[x]]++; } } } back_topsort(); go_topsort(); printf("%d\n",top); for(int i=1;i<=top;i++) printf("%d ",s[i]); putchar(10); } int main(){ freopen("i.in","r",stdin); freopen("i.out","w",stdout); read(t); while(t--) solve(); } /* 4 5 5 1 3 4 5 3 2 2 4 4 2 */
std是直接把新图建成无向图跑割点的。