USACO 6.5 章节 世界上本没有龙 屠龙的人多了也便有了
All Latin Squares
题目大意
n x n矩阵(n=2->7)
第一行1 2 3 4 5 ..N
每行每列,1-N各出现一次,求总方案数
题解
n最大为7 显然打表
写了个先数值后位置的暴搜
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "latin";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
int v[10][10]; // [row][col]
int vis[10][10]; // [val][col]
int anscnt =0;
void dfs(int val,int row){
if(val > n){
anscnt++;
return ;
}
rep(j,1,n+1){
if(v[row][j] == 0 && !vis[val][j]){
v[row][j] = 1;
vis[val][j] = 1;
if(row+1 < n){
dfs(val,row+1);
}else{
dfs(val+1,1);
}
vis[val][j] = 0;
v[row][j] = 0;
}
}
}
int main(){
// usefile();
cin>>n;
rep(i,1,n+1){
v[0][i]=i;
vis[i][i]=1;
}
dfs(1,1);
cout<<anscnt<<endl;
return 0;
}
6能搜出来,7要等好久,考虑改算法
但是数列嘛,当然先上OEIS看看http://oeis.org/search?q=1%2C2%2C24%2C1344%2C1128960&sort=&language=english&go=Search
7的时候答案是12198297600,看来直接暴搜是不太可能
考虑 一个成功的方案, 把它的非首行的两行交换,也是合法的
所以考虑第一列也定为1到N,最后乘上(N-1)!
这样计算的次数是16942080
在我i7-7700HQ上跑是
> time echo 7 | ./6.5.1
12198297600
real 0m17.194s
user 0m17.190s
sys 0m0.006s
固定第一列的代码
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "latin";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
int v[10][10]; // [row][col]
// [0->n-1][1->n]
int vis[10][10]; // [val][col]
long long anscnt =0;
void dfs(int val,int row){
if(val > n){
anscnt++;
return ;
}
if(val == row+1){
if(row+1 < n){
dfs(val,row+1);
}else{
dfs(val+1,1);
}
return ;
}
rep(j,1,n+1){
if(v[row][j] == 0 && !vis[val][j]){
v[row][j] = 1;
vis[val][j] = 1;
if(row+1 < n){
dfs(val,row+1);
}else{
dfs(val+1,1);
}
vis[val][j] = 0;
v[row][j] = 0;
}
}
}
int main(){
// usefile();
cin>>n;
rep(j,1,n+1){
v[0][j]=j;
vis[j][j]=1;
}
rep(i,1,n){
v[i][1]=i+1;
vis[i+1][1]=1;
}
dfs(1,1);
rep(i,1,n){
anscnt*=i;
}
cout<<anscnt<<endl;
return 0;
}
网上搜了一下方法,有一种是 靠循环节 置换圈 大概群论相关
而我决定 放弃,打表吧XD
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "latin";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
map<int,long long >ans;
int main(){
usefile();
ans[2]= 1;
ans[3]= 2;
ans[4]= 24;
ans[5]= 1344;
ans[6]= 1128960;
ans[7]= 12198297600;
cin>>n;
cout<<ans[n]<<endl;
return 0;
}
Closed Fences
题目大意
计算几何
逆时针给一系列点(<=200个),坐标范围(| |<=2^16)检查 这些点是否能围成一个围栏
如果可行,再给一点 输出 从该点发出射线,能照到的线段 (只要部分能被照到 就输出整段)
如果 给的点,和一条线段共线,视作无法照到
题解
看上去像是可以枚举,按照角度,200次遍历射线,每次射线遍历200条边,一共就n方左右的复杂度
我们先 过一边所有线段如果不相交,那么可以围成(这里没有判断顺时针还是逆时针)
然后枚举所有线段,对每一条线段1000等分,枚举从给定的点 向等分点是否与其它线段有交点
时间复杂度O(n*n*1000);
[这题的核心还是说写计算几何的算法
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "fence4";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
pair<int,int> p;
pair<int,int> P[210];
int N;
double cross(pair<double,double> a,pair<double,double> b){
return a.first*b.second-a.second*b.first;
}
pair<double,double> operator + (pair<double,double> a,pair<double,double> b) {
return {a.first+b.first,a.second+b.second};
}
pair<double,double> operator - (pair<double,double> a,pair<double,double> b) {
return {a.first-b.first,a.second-b.second};
}
bool isIntersect(pair<pair<double,double>,pair<double,double>> line1,pair<pair<double,double>,pair<double,double>> line2){
return !(
(cross(line1.first -line2.first,line2.second-line2.first) > 0) ==
(cross(line1.second-line2.first,line2.second-line2.first) > 0) ||
(cross(line2.first -line1.first,line1.second-line1.first) > 0) ==
(cross(line2.second-line1.first,line1.second-line1.first) > 0)
);
}
bool isValid(){
rep(i,0,N){
rep(j,i+2,N){
if(i == 0 && j == N-1)continue;
if(isIntersect({P[i],P[i+1]}, {P[j],P[(j+1)%N]})){
return false;
}
}
}
return true;
}
// 共线
bool isColine(int idx){
return cross(P[idx]-P[(idx+1)%N],p) == cross(P[idx],P[(idx+1)%N]);
}
bool isSeen(int idx){
int x1 = P[idx].first;
int y1 = P[idx].second;
int nsegments = 1000;
rep(i,1,nsegments){
pair<double,double> point_ = {
x1 + i * 1.0 / nsegments * (P[(idx + 1) % N].first - x1),
y1 + i * 1.0 / nsegments * (P[(idx + 1) % N].second - y1)};
bool blocked = false;
rep(j,0,N){
if(j == idx) {
continue;
}
if(isIntersect({p,point_}, {P[j],P[(j+1)%N]})){
blocked = true;
break;
}
}
if(!blocked) {
return true;
}
}
return false;
}
vector<int> ans;
int main(){
usefile();
scanf("%d", &N);
scanf("%d %d", &p.first,&p.second);
rep(i,0,N){
scanf( "%d %d", &P[i].first, &P[i].second);
}
if(!isValid()) {
printf( "NOFENCE\n");
return 0;
}
rep(i,0,N){
if(!isColine(i) && isSeen(i)) {
ans.push_back(i);
}
}
if(ans.size() >= 2 && ans[ans.size() - 2] == N - 2){
ans[ans.size() - 2] = N - 1;
ans[ans.size() - 1] = N - 2;
}
printf("%d\n", int(ans.size()));
rep(i,0,ans.size()){
if(ans[i] == N - 1) {
printf("%d %d %d %d\n", P[0].first, P[0].second, P[ans[i]].first, P[ans[i]].second);
}else{
printf( "%d %d %d %d\n", P[ans[i]].first, P[ans[i]].second, P[ans[i] + 1].first, P[ans[i] + 1].second);
}
}
return 0;
}
Betsy's Tour
题目大意
n x n(n<=7) 的矩阵,从左上走到坐下,每个格子最多经过一次的方案数
题解
暴搜+打表
插头dp ? 像是
实现了一下 果然过了,注意处理n = 1
插头dp的话 直接百度文库 应该能搜到不少文章,我感觉我的边界处理和状态转移的代码写得好丑QAQ
- 这里的一个技巧是,想象在左边在多出一列,把这列从上连到下,这样原题就变成确定了一部分路线的欧拉回路的插头dp了,这样 起始和终点的就也和中间的过程块,看作联通其它两个块的 来处理了
- 状态表示,最无脑的是,相同数字,但是 这样的话状态转移会比较难写 比如
100120332,注意到我们会一直维持合法性,所以可以用左右括号表示(__)(_()),这样的化就是3进制即可,然后4进制更容易操作,比如我下面用的位运算<<2 或 >>2 - 在上面两个优化下 初始状态是
(______)这样的
时间复杂度O(i*j*state) = O(7*7*3^7)
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "betsy";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
// 中间的 每个块 连接 两个方向,
map<pair<int,long long>,long long> res;
int get_state(long long state,int pos){
return (state >> (2*pos)) % 4;
}
int set_state(long long state,int pos){
return state << (2*pos);
}
// (( ) ())
// block: 的2进制位 0b3210
// 3
// 0 2
// 1
long long insert_state(long long old_state,int pos,int block){
int arr[10];
// 栈计算括号对
int p[10];
int stk[10];
int stki = 0;
rep(i,0,n+1){
arr[i] = get_state(old_state,i);
if(arr[i] == 0)continue;
if(arr[i] == 1)stk[stki++]=i;
if(arr[i] == 2){
p[i] = stk[stki-1];
p[stk[stki-1]] = i;
stki--;
}
}
int cnt = 0;
rep(i,0,4){
cnt+= !!(block & (1<<i));
}
assert(cnt == 2);
// 插入的块和 被插入的位置 同时有或没有
if( (!(block & 0b1000) != !arr[pos]) || (!(block & 0b0001) != !arr[pos+1]) ){
return -1;
}
// 原来为空 新建边
if( (block & 0b0100) && (block & 0b0010)){
arr[pos] = 1;
arr[pos+1] = 2;
}else if( (block & 0b0100) || (block & 0b0010) ){ // 引出一条原来的边
if(block & 0b0100){
arr[pos] = arr[pos]+arr[pos+1];
arr[pos+1] = 0;
}else {
arr[pos+1] = arr[pos]+arr[pos+1];
arr[pos] = 0;
}
}else{ // 把原来两段 连接起来
if(arr[pos] == 1 && arr[pos+1] == 2){
arr[pos] = 0 ;
arr[pos+1] = 0 ;
}else{
arr[pos] = 0;
arr[pos+1] = 0;
int p1 = p[pos];
int p2 = p[pos+1];
if(p1 > p2)swap(p1,p2);
arr[p1] = 1;
arr[p2] = 2;
}
}
long long new_state = 0;
rep(i,0,n+1){
new_state += set_state(arr[i],i);
}
return new_state;
}
long long dp(int idxi,int idxj,long long state){
long long &ret = res[{(idxi<<3)+idxj,state}];
if(ret != 0){
return ret;
}
// 右下角允许自连
if(idxj == n-1 && idxi == n-1) {
return ret = (get_state(state,n-1) == 1 && get_state(state,n) == 2);
}
// 过程中不允许自连
//
if (get_state(state,idxi) == 1 && get_state(state,idxi+1) == 2)return 0;
rep(i,0,4){
rep(j,i+1,4){
int new_state = insert_state(state,idxi,(1<<i)+(1<<j));
if(new_state == -1)continue;
// 最后一列
if(idxj == n-1) {
if(get_state(new_state,idxi) != 0)continue;
}
// 最后一行
if(idxi == n-1){
if(get_state(new_state,idxi+1) != 0)continue;
new_state <<=2;
ret+=dp(0,idxj+1,new_state);
}else{
ret+=dp(idxi+1,idxj,new_state);
}
}
}
// printf("(%d,%d) {%lld,%lld,%lld} = %lld\n",idxi,idxj,(state)%4,(state>>2)%4,(state>>4)%4,ret);
return ret;
}
int main(){
usefile();
cin>>n;
if(n == 1){
cout<<1<<endl;
}else{
cout<<dp(0,0,set_state(1,1)+set_state(2,n))<<endl;
}
return 0;
}
总结
6.5章其实有5个题,但后面两题 我很久很久很就以前做了 就不想再做一次了,所以这里只写了3题
本文其它链接
牛客:https://blog.nowcoder.net/n/c7f0a70d69254c9180dd0b7e475d1248
github:https://yexiaorain.github.io/Blog/2019-07-07-USACO-6.5/
USACO 6.5 章节 世界上本没有龙 屠龙的人多了也便有了的更多相关文章
- 为什么我会认为SAP是世界上最好用最牛逼的ERP系统,没有之一?
为什么我认为SAP是世界上最好用最牛逼的ERP系统,没有之一?玩过QAD.Tiptop.用友等产品,深深觉得SAP是贵的有道理! 一套好的ERP系统,不仅能够最大程度承接适配企业的管理和业务流程,在技 ...
- Nivo Slider - 世界上最棒的 jQuery 图片轮播插件
Nivo Slider 号称世界上最棒的图片轮播插件,有独立的 jQuery 插件和 WordPress 插件两个版本.目前下载量已经突破 1,800,000 次!jQuery 独立版本的插件主要有如 ...
- 世界上不存在什么RedBSD,SuseBSD或者ArchBSD,Turb...
世界上不存在什么RedBSD,SuseBSD或者ArchBSD,TurboBSD之类的东西.
- hdu---(4515)小Q系列故事——世界上最遥远的距离(模拟题)
小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)T ...
- Git是目前世界上最先进的分布式版本控制系统
一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...
- 世界上最方便的SharePoint移动客户端--Rshare
Rshare我试用了一段时间,同时也测试了其他家产品,使用后的感觉是Rshare无愧于世界上最方面的SharePoint移动客户端. 1.界面设计很方便,设计中充分考虑到移动客户的使用习惯及喜好,设计 ...
- HDU-4515 小Q系列故事——世界上最遥远的距离
小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) ...
- Smartdraw世界上最流行的商业绘图软件
Smartdraw 更多图片(5张) SmartDraw是世界上最流行的商业绘图软件.2006 年获美国政府司法部 (U.S. Department of Justice) 专用软件,2007获美国政 ...
- JavaScript: 世界上最被误解的语言|Douglas Crockford
JavaScript: 世界上最被误解的语言 JavaScript: The Wrrrld's Most Misunderstood Programming Language Douglas Croc ...
随机推荐
- datepart()函数的使用
/* datepart()函数的使用 * datepart()函数可以方便的取到时期中的各个部分 *如日期:2006-07--02 18:15:36 ...
- CSS的置换和非置换元素
一个来自面试的坑. 面试的时候考官先问了行内元素和块级元素的区别,这个不难理解.然后一脚就踩进了,置换元素的坑.例如img就是行内置换元素,这种行内元素是可以设置宽高的. 什么是置换元素 一个内容不受 ...
- P3914染色计数
题目描述 有一颗\(N\)个节点的树,节点用\(1,2,\cdots,N\)编号.你要给它染色,使得相邻节点的颜色不同.有\(M\)种颜色,用\(1,2,\cdots,M\)编号.每个节点可以染\(M ...
- Emacs中的前进后退jump-tree
Emacs中的前进后退jump-tree */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #8394 ...
- python 字符编码问题总结
都是计算机存储是二进制0101之类的数字 最早计算机在美国开始的 所以数字和英文之类的占用八位 2的8次方 256可以存储对于英文和数字戳戳有余 每个国家都有自己的编码 中国 gb2312 gbk ...
- spring 配置属性细节
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/qilixiang012/article/details/28233811 概要(红色为上一篇所讲,蓝 ...
- 认识Mybatis
什么是Mybatis? MyBatis 是一款优秀的基于Java的持久层框架(来源于“internet”和“abatis”的组合),它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了 ...
- Django学习——开发你的第一个Django应用1
突然对Django热情似火,所以就开学习了,我是根据官方文档学习的,所以我打算把官方文档翻译一遍,全当学习,首先贴官方文档的地址:https://docs.djangoproject.com/en/1 ...
- python车牌精确定位
#coding=utf-8 import cv2 # 使用的是HyperLPR已经训练好了的分类器 watch_cascade = cv2.CascadeClassifier('model/casca ...
- es5和es6中的this指向问题
const test ={ id:2, a:function(){ var a_this=this; setTimeout(function(){ console.log('a:',this,a_th ...