szy学长出的,除了T3外都比较水 360/400。
T1.小猪划船
题目大意:六只猪要过河,三只大猪ABC,三只小猪abc,其中ABCa会划船,共一只船,每次可以载2个人,给出四只猪的划船耗时,每次运载花的时间是船上耗时最小的猪的耗时乘上船上猪的个数,小猪与其对应大猪不在一起时不能与其他大猪在一起,求所有猪到对岸的最小耗时。
思路:状态最多2^7(每只猪还有船的位置),随便建图最短路或者直接搜索或者构造都能通过该题。我写的O(2^21)。
#include<cstdio> #include<algorithm> using namespace std; #define INF 0x3FFFFFFF int g[130][130],t[4],a[6],b; bool check() { if(a[3]!=a[0]&&(a[3]==a[1]||a[3]==a[2]))return false; if(a[4]!=a[1]&&(a[4]==a[0]||a[4]==a[2]))return false; if(a[5]!=a[2]&&(a[5]==a[0]||a[5]==a[1]))return false; return true; } int hash() { int r=0,i; for(i=0;i<6;++i)r|=(a[i]<<i); return r|(b<<6); } int main() { freopen("boat.in","r",stdin); freopen("boat.out","w",stdout); int i,j,k,x; for(i=0;i<4;++i)scanf("%d",&t[i]); for(i=0;i<1<<7;++i)for(j=0;j<1<<7;++j)if(i!=j)g[i][j]=INF; for(i=0;i<1<<7;++i) { for(j=0;j<6;++j)a[j]=bool(i&(1<<j));b=bool(i&(1<<6)); if(!check())continue; for(j=0;j<4;++j)if(a[j]==b) { a[j]^=1;b^=1; if(check())x=hash(),g[i][x]=min(g[i][x],t[j]); a[j]^=1;b^=1; for(k=0;k<6;++k)if(j!=k&&a[k]==b) { if(j==3&&(k==1||k==2))continue; a[j]^=1;a[k]^=1;b^=1; if(check())x=hash(),g[i][x]=min(g[i][x],t[j]<<1); a[j]^=1;a[k]^=1;b^=1; } } } for(k=0;k<1<<7;++k)for(i=0;i<1<<7;++i)for(j=0;j<1<<7;++j) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); printf("%d",g[0][127]); fclose(stdin);fclose(stdout);return 0; }
T2.小猪星球
题目大意:给出N个点M条边的图,边权可能为负数,可以一次把所有边的边权加上同一个数,求出1到n非负的最短路。(N<=100,M<=N(N+1)/2)
思路:以前的原题。二分加上多少,SPFA check。
#include<cstdio> #include<cstring> #define MN 100 #define ME 5050 #define INF 100000 struct edge{int nx,t,w;}e[ME*2+5]; int h[MN+5],rh[MN+5],en,u[MN+5],q[MN+5],qn,d[MN+5],z[MN+5]; void ins(int*h,int x,int y,int w){e[++en]=(edge){h[x],y,w};h[x]=en;} int spfa(int x,int c) { if(z[x])return 1;z[x]=1; for(int i=h[x];i;i=e[i].nx)if(u[e[i].t]&&d[x]+e[i].w+c<d[e[i].t]) {d[e[i].t]=d[x]+e[i].w+c;if(spfa(e[i].t,c))return 1;} return z[x]=0; } bool check(int x) { memset(d,42,sizeof(d)); memset(z,d[1]=0,sizeof(z)); return !spfa(1,x); } int main() { freopen("planet.in","r",stdin); freopen("planet.out","w",stdout); int T,n,m,x,y,w,i,j,l,r,mid,ans; for(scanf("%d",&T);T--;) { scanf("%d%d",&n,&m); memset(h,en=0,sizeof(h));memset(rh,0,sizeof(rh)); while(m--) { scanf("%d%d%d",&x,&y,&w); ins(h,x,y,w);ins(rh,y,x,w); } memset(u,0,sizeof(u)); for(u[q[i=qn=0]=n]=1;i<=qn;++i) for(j=rh[q[i]];j;j=e[j].nx)if(!u[e[j].t])u[q[++qn]=e[j].t]=1; if(!u[1]){puts("-1");continue;} for(l=-INF,r=INF;l<=r;) if(check(mid=l+r>>1)&&d[n]>=0)r=mid-1,ans=d[n]; else l=mid+1; printf("%d\n",ans); } fclose(stdin);fclose(stdout);return 0; }
T3.小猪送货
题目大意:N个点,源到点i连一条流量pi的边,点i到汇连流量si的边,对所有i<j的i和j连流量c的单向边,求最大流。(N<=10000)
思路:CF原题。暴力60分。考虑转成最小割DP,由于点与点之间边的流量相同,可以用f[i][j]表示前i个点j个割S,i-j个割T的最小割,可以得到DP方程f[j]=min(f[j]+p[i]+c*j,f[j-1]+s[i]),复杂度O(n^2)。
暴力
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<cstring> inline int read() { int x=0;char c; while((c=getchar())<'0'||c>'9'); for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 100 #define ME 5150 #define S MN+1 #define T MN+2 #define INF 0x7FFFFFFF struct edge{int nx,t,w;}e[ME*2+5]; int h[MN+5],en=1,d[MN+5],q[MN+5],qn,c[MN+5]; long long ans; inline void ins(int x,int y,int w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,0};h[y]=en; } bool bfs() { int i,j; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx) if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; return d[T]; } int dfs(int x,int r) { if(x==T)return r; int u=0,k; for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1) { k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w); u+=k;e[i].w-=k;e[i^1].w+=k; if(u==r)return r; } return d[x]=0,u; } int main() { freopen("deliver.in","r",stdin); freopen("deliver.out","w",stdout); int n=read(),c=read(),i,j; for(i=1;i<=n;++i)ins(S,i,read()); for(i=1;i<=n;++i)ins(i,T,read()); for(i=1;i<n;++i)for(j=i+1;j<=n;++j)ins(i,j,c); while(bfs())ans+=dfs(S,INF); printf("%I64d",ans); fclose(stdin);fclose(stdout);return 0; }
正解
#include<cstdio> #include<algorithm> using namespace std; #define ll long long inline int read() { int x=0;char c; while((c=getchar())<'0'||c>'9'); for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0'; return x; } #include<vector> #define MN 10000 #define INF (1LL<<60) int a[MN+5],b[MN+5]; ll f[MN+5],ans=INF; int main() { int n=read(),c=read(),i,j; for(i=1;i<=n;++i)a[i]=read(); for(i=1;i<=n;++i)b[i]=read(); for(i=1;i<=n;++i)for(j=i;j>=0;--j) f[j]=min(j<i?f[j]+a[i]+(ll)c*j:INF,j?f[j-1]+b[i]:INF); for(i=0;i<=n;++i)ans=min(ans,f[i]); printf("%I64d",ans); }
T4.小猪数数
题目大意:两个人玩游戏,一开始各有一个1,每次可以选择一个玩家,让他的数加上对方的数,给出N,M,问最少进行几轮游戏到达当前局面,不可能则输出-1。(N,M在long long范围内)
思路:若n>m,则n只能是由n-m加上m得到的,故可以用求gcd的辗转相除法处理,若求出的gcd不为1则输出-1,另外数据可能有0或负数,要特判……复杂度O(logN+logM)。
#include<cstdio> #define ll long long ll a,b,ans; ll gcd(ll a,ll b){return b?(ans+=a/b,gcd(b,a%b)):a;} int main() { freopen("math.in","r",stdin); freopen("math.out","w",stdout); scanf("%I64d%I64d",&a,&b); if(a<1||b<1||gcd(a,b)!=1)puts("-1"); else printf("%I64d",ans-1); fclose(stdin);fclose(stdout);return 0; }