UOJ#109. 【APIO2013】TASKSAUTHOR

FloydWarshall
Gamble1
ModifiedDijkstra
OptimizedBellmanFord
RecursiveBacktracking
题目:http://uoj.ac/problem/109
思路:第一次做提交答案题,做了整整一天
其主要难点在于 subtask4,6 ,其他的都很快解决。
subtask1 :
注意到 FloydWarshall 的运行只依赖于点的个数,而 ModifiedDijkstra 并不是如此,我们直接构造一张 V=101 的空图就好了。
代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int n;
int main(){
    freopen("task1.in","w",stdout);
    n = 101;
    cout<<n<<endl;
    for (int i = 1;i <= n; ++i) puts("0");
    cout<<1<<endl;
    cout<<1<<" "<<1<<endl;
    fclose(stdout);
    return 0; 
} 

subtask2 :
注意到 OptimizedBellmanFord 的最坏复杂度是 O(VE) ,我们想让它达到这个复杂度,必须每次松弛的每个点都被更新,而且每次单点的值都被更新,也就是说每次只有一个点达到 mindist ,这样的话注意到循环顺序是 0 n1我们可以从 n 1引一条链,边权都为 0 ,这样能保证每次只更新一个点,然后我们为了让E更大剩下的暴力随便连就好了。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int n,num;
int main(){
    freopen("task2.in","w",stdout);
    n = 100;
    num = 0;
    cout<<n<<endl;
    cout<<0<<endl;
    cout<<70<<" "<<0<<" "<<0<<" "; 
    for (int i = 1;i <= 69; ++i) cout<<0<<" "<<1<<" ",num += 2;
    cout<<endl;
    for (int i = 2;i < n; ++i)
       {  cout<<10<<" "<<i - 1<<" "<<0<<" ";
          num += 3;
          for (int j = 1;j <= 9; ++j) cout<<i - 1<<" "<<1<<" ",num += 2;
       cout<<endl; 
       }
    cout<<10<<endl;
    for (int i = 1;i <= 10; ++i) cout<<n - 1<<" "<<0<<endl,num += 2;
    cout<<num + 6<<endl;
       fclose(stdout);
    return 0;
}

subtask3 :
subtask1 一样,还是卡点数

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int n;
int main(){
    freopen("task3.in","w",stdout);
    n = 101;
    cout<<n<<endl;
    for (int i = 1;i <= n; ++i) cout<<0<<endl;
    cout<<1<<endl;
    cout<<1<<" "<<1;
    fclose(stdout);
    return 0;
}

subtask4,6
这两个点是可以一起搞的,构造思路比较新颖,卡了我好久。
考虑 ModifiedDijkstra 的复杂度是依赖于没有负边的,又注意到我们想让一个点不断被更新,那么根据松弛原理,我们连 (0,1,1)(0,2,2)(2,1,2) 这样的基本图形会导致 2 被更新两遍,于是我们暴力的把这样的基本图形串起来,对于这个图形如果想要得到指数数量级的话必须有限制,我们把它想象成一个实现的堆栈,不断上下递归,那么对于(2,1,2)这类的边,要求其边权肯定大于后缀和,这样就符合题解中的 2i 的倍增思路了,然而我非常 zz ,利用这个性质直接递推出边权,但一开始各种炸权值上限,最后随机限制了一下边权才过。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
int V,E,n,num,sum,d,f[100];
int main(){
    //freopen("task4.in","w",stdout);
    n = 16;
    num = 0;
    V = 2 * n + 1; 
    cout<<V<<endl;
    f[n - 1] = -2; sum = -2;
    d = 2;
    for (int i = n - 2;i >= 0; --i){
        f[i] = sum * d;
        sum += f[i];
        if (i == 0) f[i] = -999999;
        if (sum < -10000) sum += 5000;
        else
        if (sum < -2500) sum += 1250;
        else 
        if (sum < -50) sum += 25;
    } 
    for (int i = 0;i < n; ++i){
        cout<<2<<" "<<2 * i + 2<<" "<<-2<<" "<<2 * i + 1<<" "<<-1<<endl;
        num +=5;
        if (i != n - 1) cout<<1<<" "<<2 * i + 2<<" "<<f[i]<<endl,num += 3;
        else cout<<0<<endl,num += 1;
    }
    cout<<0<<endl;
    cout<<7<<endl;
    for (int i = 1;i <= 7; ++i) cout<<0<<" "<<V - 1<<" "<<endl,num += 2;
    cout<<num + 3<<endl;
    fclose(stdout);
}

subtask5 :
我们只需要修改 subtask2 ,各种尝试限制边的个数,注意到在这里 V 越大效果越好。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int n,num;
int main(){
    freopen("task5.in","w",stdout);
    num = 0;
    n = 290;
    cout<<n<<endl;
    cout<<0<<endl;
    for (int i = 1;i < n; ++i)
       {  if (i > 230) cout<<2<<" "<<i - 1<<" "<<0<<" ";
          else  cout<<1<<" "<<i - 1<<" "<<0<<" ";
          num += 3;
          if (i > 230) for (int j = 1;j <= 1; ++j) cout<<i - 1<<" "<<1<<" ",num += 2;
       cout<<endl; 
       }
    cout<<10<<endl;
    for (int i = 1;i <= 10; ++i) cout<<n - 1<<" "<<0<<endl;
    //cout<<num + 23<<endl;
       fclose(stdout);
    return 0;
}

subtask7:
为了让暴搜非常慢我们可以尽可能的构造一张大的完全图,这样就能过了。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int V,E,num;
int main(){
    freopen("task7.in","w",stdout);
    V = 999; E = 1501;
    num = 0;
    cout<<V<<" "<<E<<endl;
    for (int i = 0;i <= 54; ++i)
      for (int j = 0;j < i; ++j)
        cout<<i<<" "<<j<<endl,num+=2;
    for (int i = 1;i <= 16; ++i) cout<<V - i<<" "<<V - i - 1<<endl,num += 2;
    //cout<<num + 2;
        fclose(stdout);
    return 0;
}

subtask8 :
8和9都是染色问题,要求相邻的颜色不相同,给出了一个暴搜程序。
为了让它过掉又尽可能边数满足条件,这样构造:
构造 249 个点数为 4 的完全图,然后剩下3个也做一个完全图
最后将三个点每个点向其中一个四个点的完全图连边,要求各不相同且有一个3-完全图点连了 2 <script type="math/tex" id="MathJax-Element-33">2</script>个4-完全图的点。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
int V,E,num;
int main(){
    freopen("task8.in","w",stdout);
    V = 999; E = 1501;
    cout<<V<<" "<<E<<endl;
    num = 0;
    for (int i = 0;i < V - 3; ++i){
        if (i % 4 == 1) cout<<i<<" "<<i - 1<<endl,num += 2;
        if (i % 4 == 2) cout<<i<<" "<<i - 1<<endl<<i<<" "<<i - 2<<endl,num += 4;
        if (i % 4 == 3) cout<<i<<" "<<i - 1<<endl<<i<<" "<<i - 2<<endl<<i<<" "<<i - 3<<endl,num += 6; 
    }
    cout<<V - 1<<" "<<V - 2<<endl<<V - 1<<" "<<V - 3<<endl<<V - 2<<" "<<V - 3<<endl;
    cout<<V - 1<<" "<<V - 4<<endl<<V - 2<<" "<<V - 5<<endl<<V - 3<<" "<<V - 6<<endl<<V - 1<<" "<<V - 7<<endl;
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值