题目:https://loj.ac/problem/2557
第一个点可以暴搜。
第三个点无依赖关系,k=3,可以 DP 。dp[ cr ][ i ][ j ] 表示前 cr 个任务、第一台机器最晚完成时间是 i 、第二台机器最晚完成时间是 j ,第三台机器最晚完成时间是多少。数组开 500 就行了。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=55,M=505,K=5,INF=1e9+5; int n,m,k,op,dp[N][M][M],pr[N][M][M][2]; int t[N][K],r[K][K],prn[N]; int main() { scanf("%d%d%d%d",&n,&m,&k,&op); for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) scanf("%d",&t[i][j]); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) scanf("%d",&r[i][j]); memset(dp,0x3f,sizeof dp); dp[0][0][0]=0; for(int cr=1;cr<=n;cr++) for(int i=0;i<=500;i++) for(int j=0;j<=500;j++) { int w=t[cr][3]; dp[cr][i][j]=dp[cr-1][i][j]+w; pr[cr][i][j][0]=i; pr[cr][i][j][1]=j; w=t[cr][1]; if(i>=w&&dp[cr-1][i-w][j]<dp[cr][i][j]) { dp[cr][i][j]=dp[cr-1][i-w][j]; pr[cr][i][j][0]=i-w; pr[cr][i][j][1]=j; } w=t[cr][2]; if(j>=w&&dp[cr-1][i][j-w]<dp[cr][i][j]) { dp[cr][i][j]=dp[cr-1][i][j-w]; pr[cr][i][j][0]=i; pr[cr][i][j][1]=j-w; } } int ans=INF,r0,r1; for(int i=0;i<=500;i++) for(int j=0;j<=500;j++) { int d=Mx(Mx(i,j),dp[n][i][j]); if(d<ans)ans=d,r0=i,r1=j; } for(int i=n;i;i--) { int t0=pr[i][r0][r1][0],t1=pr[i][r0][r1][1]; if(t0!=r0)prn[i]=1; else if(t1!=r1)prn[i]=2; else prn[i]=3; r0=t0; r1=t1; } for(int i=1;i<=n;i++)printf("%d ",prn[i]);puts(""); return 0; }
第四个点连成三条编号有序的链 。 dp[ i ][ j ] 表示前 i 个任务、此刻在 j 点,最小时间总和。链断开的位置不用算“传输时间”即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<map> using namespace std; const int N=405,K=105,INF=1e9+5; int n,m,k,op,a[N],prn[N]; int t[N][K],r[K][K],dp[N][K],pr[N][K]; bool vis[N]; int main() { scanf("%d%d%d%d",&n,&m,&k,&op); for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v); for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) scanf("%d",&t[i][j]); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) scanf("%d",&r[i][j]); for(int j=1;j<=k;j++) dp[1][j]=t[1][j]; for(int i=2;i<=n;i++) { if(i==134||i==267) { int rt=1; for(int l=2;l<=k;l++) if(dp[i-1][l]<dp[i-1][rt])rt=l; for(int j=1;j<=k;j++) { dp[i][j]=t[i][j]+dp[i-1][rt]; pr[i][j]=rt; } continue; } for(int j=1;j<=k;j++) { dp[i][j]=INF; for(int l=1;l<=k;l++) if(dp[i-1][l]+r[l][j]<dp[i][j]) { dp[i][j]=dp[i-1][l]+r[l][j]; pr[i][j]=l; } dp[i][j]+=t[i][j]; } } int ans=1; for(int j=2;j<=k;j++)if(dp[n][j]<dp[n][ans])ans=j; for(int i=n;i;i--) prn[i]=ans,ans=pr[i][ans]; for(int i=1;i<=n;i++)printf("%d ",prn[i]);puts(""); return 0; }
第七个点,发现每个任务在每个机器上的时间差不多,所以(看别人博客)发现方案应该是二分图匹配。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } const int N=505,K=605,M=N*K; int n,m,k,op,lm=1014,hd[N],xnt,to[M],nxt[M],per[K],prn[N]; int t[N][K],r[K][K]; bool vis[K]; void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;} bool xyl(int cr) { for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]) { vis[v]=1; if(!per[v]||xyl(per[v])) { per[v]=cr; return true;} } return false; } int main() { n=rdn();m=rdn();k=rdn();op=rdn(); for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) t[i][j]=rdn(); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) r[i][j]=rdn(); for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) if(t[i][j]<=lm)add(i,j); for(int i=1;i<=n;i++) { memset(vis,0,sizeof vis); xyl(i); } for(int i=1;i<=k;i++) if(per[i])prn[per[i]]=i; for(int i=1;i<=n;i++)printf("%d ",prn[i]);puts(""); return 0; }
剩下 op=1 的点,试图用模拟退火,但效果很不好,几乎没有什么改变。自己不是很了解随机化算法……
#include<cstdio> #include<cstring> #include<algorithm> #include<ctime> #include<cmath> #define db double #define ll long long using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=505,M=605,K=55; const db dec=0.988,eps=1e-5; int n,m,k,op,prn[N],a[N],b[N]; int t[N][K],r[K][K],ans; struct Ed{ int x,y; }ed[M]; int cal(bool fx) { int ret=0; for(int i=1;i<=n;i++) ret+=t[i][fx?b[i]:a[i]]; for(int i=1;i<=m;i++) { int u=ed[i].x,v=ed[i].y; ret+=t[fx?b[u]:a[u]][fx?b[v]:a[v]]; } return ret; } int get_rd(db T) { ll ret=(rand()*2-RAND_MAX)*T; return ret%k; } void SA(db T) { int ys=cal(0),ts; int deb=0; while(T>eps) { int p=rand()%n+1,d=a[p]+get_rd(T), cnt=0; while(d==a[p]&&cnt<20)d=a[p]+get_rd(T),cnt++; d=Mx(d,1); d=Mn(d,k); b[p]=d; ts=cal(1); if(ts<ys||exp((ts-ys)/T)*RAND_MAX<rand()) { a[p]=d; ys=ts; if(ts<ans)memcpy(prn,a,sizeof a); } b[p]=a[p]; T*=dec; } } int main() { srand(time(0)); n=rdn();m=rdn();k=rdn();op=rdn(); for(int i=1;i<=m;i++) { ed[i].x=rdn();ed[i].y=rdn();} for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) t[i][j]=rdn(); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) r[i][j]=rdn(); for(int i=1;i<=n;i++)a[i]=b[i]=prn[i]=rand()%k+1; ans=cal(0); for(int i=1;i<=50;i++) SA(10000); for(int i=1;i<=n;i++)printf("%d ",prn[i]);puts(""); return 0; }