LOJ 2557 「CTSC2018」组合数问题 (46分)

题目: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;
}
View Code

第四个点连成三条编号有序的链 。 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;
}
View Code

第七个点,发现每个任务在每个机器上的时间差不多,所以(看别人博客)发现方案应该是二分图匹配。

#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;
}
View Code

剩下 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;
}
View Code

 

posted on 2019-05-08 16:25  Narh  阅读(342)  评论(0编辑  收藏  举报

导航