• 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
  • 相关阅读:
    T-Sql语法:行转列(pivot)和列转行(unpivot)
    T-Sql语法:GROUP BY子句GROUPING SETS、CUBE、ROLLUP
    Asp.net使用Plupload上传组件详解
    form标签属性enctype之multipart/form-data请求详解
    基于Owin Oauth2构建授权服务器
    AutoFac使用~IOC容器(DIP,IOC,DI)
    第二节:模型(Models)和管理后台(Admin site)
    第三节:视图(Views)和模板(Templates)
    THINKPHP 3.2 PHP SFTP上传下载 代码实现方法
    Linux 上导出导入sql文件到服务器命令
  • 原文地址:https://www.cnblogs.com/Narh/p/10832674.html
Copyright © 2020-2023  润新知