題目大意:給出一組木棍,若兩木棍相交(規範相交,非規範相交部分去除重疊部分)釘一個釘子;
若有木棍和其他所有木棍不相交,釘兩個;最後固定所有木棍,所需釘子數;
解題策略:很水的一題,判斷線段相交,而且相交情況簡單,沒什麼說的。
之前題意理解複雜,YY一下題意,並給出程序,有興趣的看下文:
原版題意代碼:
/*
UVA 11783 Nail
AC by J_Dark
ON 2013/5/4
Time 0.295s
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
struct point{
double x, y;
point(double p=0, double q=0){
x = p;
y = q;
}
};
struct line{
point a, b;
bool use;
line(point x, point y){
a = x;
b = y;
use = false;
}
};
vector<line> Seg;
int segNum;
const double eps = 1e-20;
//////////////////////////////////
void Input(){
Seg.clear();
double ax, ay, bx, by;
for(int i=0; i<segNum; i++){
cin >> ax >> ay >> bx >> by;
point start(ax, ay), end(bx, by);
Seg.push_back(line(start, end));
}
}
double Direction(point Pi, point Pj, point Pk){
return (Pj.x-Pi.x)*(Pk.y-Pi.y)-(Pk.x-Pi.x)*(Pj.y-Pi.y);
}
//判斷pk點是否在線段pi-pj上
bool OnSegment(point Pi, point Pj, point Pk){
if(min(Pi.x, Pj.x) <= Pk.x && max(Pi.x, Pj.x) >= Pk.x &&
min(Pi.y, Pj.y) <= Pk.y && max(Pi.y, Pj.y) >= Pk.y)
return true;
return false;
}
bool isIntersect(line p, line q){
double d1, d2, d3, d4;
d1 = Direction(p.a, p.b, q.a);
d2 = Direction(p.a, p.b, q.b);
d3 = Direction(q.a, q.b, p.a);
d4 = Direction(q.a, q.b, p.b);
//規範相交
if( d1*d2<0 && d3*d4<0) { return true;}
//非規範相交
else if(fabs(d1)<eps && OnSegment(p.a, p.b, q.a)) {return true;}
else if(fabs(d2)<eps && OnSegment(p.a, p.b, q.b)) {return true;}
else if(fabs(d3)<eps && OnSegment(q.a, q.b, p.a)) {return true;}
else if(fabs(d4)<eps && OnSegment(q.a, q.b, p.b)) {return true;}
else return false;
}
void Compute(){
int ansNail = 0;
point t;
for(int i=0; i<segNum; i++){
for(int k=i+1; k<segNum; k++){
if( isIntersect(Seg[i], Seg[k]) ){
Seg[i].use = Seg[k].use = true; //標記木棍已釘住
ansNail++;
}
}
if(!Seg[i].use) ansNail += 2; //木棍未被釘子釘住
}
cout << ansNail << endl;
}
//////////////////////////////////
int main(){
while(cin >> segNum && segNum)
{
Input();
Compute();
}
return 0;
}
YY加強版題意:在原版題目題意基礎上,增加一個條件:
1,若存在多個木棍交與一點,若之前該交點被當前木棍之前的木棍使用過,該交點無效!不能繼續釘釘子!求最後需要釘子數目。
2,原題中不存在上述數據,所以題目瞬間成水題,大家看原題目時看到這張圖片沒:
3,人生就是想太多——周B捷童鞋如是說
當前題意下,釘子數ansNail = 1 + 2 = 3。按照這個題意理解的代碼,怎麼解決,大家思考吧,實際也很簡單,歡迎大家拍磚:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
struct point{
double x, y;
point(double p=0, double q=0){
x = p;
y = q;
}
};
struct line{
point a, b;
bool use;
line(point x, point y){
a = x;
b = y;
use = false;
}
};
vector<line> Seg;
vector<point> Nail;
int segNum;
const double eps = 1e-20;
//////////////////////////////////
void Input(){
Seg.clear();
Nail.clear();
double ax, ay, bx, by;
for(int i=0; i<segNum; i++){
cin >> ax >> ay >> bx >> by;
point start(ax, ay), end(bx, by);
Seg.push_back(line(start, end));
}
}
//判斷是否爲有效Nail
bool isCorrectNail(point t){
vector<point>::iterator it;
for(it=Nail.begin(); it!=Nail.end(); it++){
//交點之前出現
if(fabs((*it).x - t.x)<eps && fabs((*it).y - t.y)<eps){
return false;
}
}
//交點之前未出現
Nail.push_back(t);
return true;
}
double Direction(point Pi, point Pj, point Pk){
return (Pj.x-Pi.x)*(Pk.y-Pi.y)-(Pk.x-Pi.x)*(Pj.y-Pi.y);
}
//判斷pk點是否在線段pi-pj上
bool OnSegment(point Pi, point Pj, point Pk){
if(min(Pi.x, Pj.x) <= Pk.x && max(Pi.x, Pj.x) >= Pk.x &&
min(Pi.y, Pj.y) <= Pk.y && max(Pi.y, Pj.y) >= Pk.y)
return true;
return false;
}
bool isIntersect(line p, line q, point &t){
double d1, d2, d3, d4;
d1 = Direction(p.a, p.b, q.a);
d2 = Direction(p.a, p.b, q.b);
d3 = Direction(q.a, q.b, p.a);
d4 = Direction(q.a, q.b, p.b);
//規範相交
if( d1*d2<0 && d3*d4<0){
t.x = (q.a.x*d2 - q.b.x*d1)/(d2 - d1);
t.y = (q.a.y*d2 - q.b.y*d1)/(d2 - d1);
return true;
}
//非規範相交
else if(fabs(d1)<eps && OnSegment(p.a, p.b, q.a)) {t = q.a; return true;}
else if(fabs(d2)<eps && OnSegment(p.a, p.b, q.b)) {t = q.b; return true;}
else if(fabs(d3)<eps && OnSegment(q.a, q.b, p.a)) {t = p.a; return true;}
else if(fabs(d4)<eps && OnSegment(q.a, q.b, p.b)) {t = p.b; return true;}
else return false;
}
void Compute(){
int ansNail = 0;
bool ff1, ff2;
point t;
for(int i=0; i<segNum; i++){
for(int k=i+1; k<segNum; k++){
ff1 = isIntersect(Seg[i], Seg[k], t);
if(ff1){ //線段相交
if(isCorrectNail(t)){ //若交點未被線段i之前的釘子使用
Seg[i].use = Seg[k].use = true; //標記線段已釘住
ansNail++;
}
}
}
if(!Seg[i].use) ansNail += 2; //未被釘子釘住
}
cout << ansNail << endl;
}
//////////////////////////////////
int main(){
while(cin >> segNum && segNum)
{
Input();
Compute();
}
return 0;
}